超簡單學會使用 C++ 單元測試框架,再也不怕面試官刁難了

導讀

  C++ 開發時我們常有一個非常期望的願景,那就是引用第三方庫和框架時希望儘可能的簡單,不然各種平臺、各種編譯問題可以讓人焦頭爛額。而Catch2就是一個只有頭文件的單元測試框架。放心,這個單元測試框架完全能夠支撐你的項目,且它的協議是 Boost Software License,完全可以商用。

在這裏插入圖片描述
由於Catch2只有一個頭文件,因此你只需要下載這個頭文件下來,添加到你的項目中就可以了。

github 下載地址:catchorg/Catch2

不能翻牆的提供csdn下載:
catchorg/Catch2

使用案例

#define CATCH_CONFIG_MAIN

#include <catch2/catch.hpp>

int Factorial( int number ) {
   return number <= 1 ? number : Factorial( number - 1 ) * number;  // fail
// return number <= 1 ? 1      : Factorial( number - 1 ) * number;  // pass
}

TEST_CASE( "Factorial of 0 is 1 (fail)", "[single-file]" ) {
    REQUIRE( Factorial(0) == 1 );
}

TEST_CASE( "Factorials of 1 and higher are computed (pass)", "[single-file]" ) {
    REQUIRE( Factorial(1) == 1 );
    REQUIRE( Factorial(2) == 2 );
    REQUIRE( Factorial(3) == 6 );
    REQUIRE( Factorial(10) == 3628800 );
}

這個例子是官方案例,演示了計算階乘的算法。

常規測試

#define CATCH_CONFIG_MAIN 這個宏定義了catch2 的 main 函數。下面的代碼是從 catch2 裏面摘抄的,可以看到 main 函數定義。

#ifdef CATCH_CONFIG_MAIN
// start catch_default_main.hpp

#ifndef __OBJC__

#if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN)
// Standard C/C++ Win32 Unicode wmain entry point
extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) {
#else
// Standard C/C++ main entry point
int main (int argc, char * argv[]) {
#endif

    return Catch::Session().run( argc, argv );
}

意味着你不需要自己寫 main 函數。

但是如果你需要寫自己的 main 函數,catch2 也支持,像下面這樣:

#define CATCH_CONFIG_RUNNER
#include "catch.hpp"

int main( int argc, char* argv[] ) {
  // global setup...

  int result = Catch::Session().run( argc, argv );

  // global clean-up...
  return result;
}

大多數情況下我們都是有 main 函數的,當你運行項目時Catch::Session().run( argc, argv );這一句就啓動了你的單元測試。如下:

#define CATCH_CONFIG_MAIN
#include "catch.hpp"

int main(int argc, char* argv[]) {
    // global setup...
    int result = Catch::Session().run(argc, argv);
    // global clean-up...
    return result;
}

int Factorial(int number) {
    return number <= 1 ? number : Factorial(number - 1) * number;  // fail
}

TEST_CASE("Factorial of 0 is 1 (fail)", "[single-file]") {
    REQUIRE(Factorial(0) == 1);
}

TEST_CASE("Factorials of 1 and higher are computed (pass)", "[single-file]") {
    REQUIRE(Factorial(1) == 1);
    REQUIRE(Factorial(2) == 2);
    REQUIRE(Factorial(3) == 6);
    REQUIRE(Factorial(10) == 3628800);
}

回到最上面的測試案例,我們的 test-case 是有命名的,其實 test-case 也是可以沒有名字的,因爲要測試的函數多了,你最終總是要命名。不命名的test-case 如下:

TEST_CASE() {
    REQUIRE(Factorial(1) == 1);
}

簡單吧。 剩下的就是去補充你的單元測試文件了,直到你寫完了自己需要的 test-case。

看下單元測試運行結果:

在這裏插入圖片描述
16 行測試沒有通過,和代碼相符。

BDD 風格測試

BDD 簡介:

Behavior Driven Development,行爲驅動開發是一種敏捷軟件開發的技術,它鼓勵軟件項目中的開發者、QA和非技術人員或商業參與者之間的協作,BDD 提倡的是通過將測試語句轉換爲類似自然語言的描述,開發人員可以使用更符合大衆語言的習慣來書寫測試,當別人接手/交付,或者自己修改的時候,都簡單易明白,順利很多。一個典型的 BDD 測試用例包括完整的三段式上下文,測試大多可以翻譯爲Given…When…Then的格式,讀起來輕鬆愜意。

catch2 也支持像 BDD(行爲驅動開發)風格的單元測試。

因爲我們目前的項目是採用敏捷開發的模式,同時基於 BDD 開發,需求人員在寫需求是是按照 GIVEN WHEN THEN 的方式,截張圖看下(真實圖片,理解打碼):

在這裏插入圖片描述

所以我們的單元測試需要跟隨 BDD 流程來。這樣可以保證所有參與者的理解是一致的,包括代碼也是跟隨需求描述是一致的。

案例:

#define CATCH_CONFIG_MAIN
#include "catch.hpp"

SCENARIO( "vectors can be sized and resized", "[vector]" ) {

    GIVEN( "A vector with some items" ) {
        std::vector<int> v( 5 );

        REQUIRE( v.size() == 5 );
        REQUIRE( v.capacity() >= 5 );

        WHEN( "the size is increased" ) {
            v.resize( 10 );

            THEN( "the size and capacity change" ) {
                REQUIRE( v.size() == 10 );
                REQUIRE( v.capacity() >= 10 );
            }
        }
        WHEN( "the size is reduced" ) {
            v.resize( 0 );

            THEN( "the size changes but not capacity" ) {
                REQUIRE( v.size() == 0 );
                REQUIRE( v.capacity() >= 5 );
            }
        }
        WHEN( "more capacity is reserved" ) {
            v.reserve( 10 );

            THEN( "the capacity changes but not the size" ) {
                REQUIRE( v.size() == 5 );
                REQUIRE( v.capacity() >= 10 );
            }
        }
        WHEN( "less capacity is reserved" ) {
            v.reserve( 0 );

            THEN( "neither size nor capacity are changed" ) {
                REQUIRE( v.size() == 4 );
                REQUIRE( v.capacity() >= 5 );
            }
        }
    }
}

真實項目不便演示,上面的代碼也是官方給的 BDD 風格的單元測試。我在 42 行故意寫錯,看下運行結果:
在這裏插入圖片描述

結果提示 42 行測試不通過。

結語

基於上面的小案例,我相信你已經迅速掌握了 catch2 的用法,也可以給自己的代碼寫單元測試了。只有當你真正的實踐過,你才能知道這裏面的門道。

如有幫助,請多多點贊支持。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章