【解鎖】Catch2——C++測試框架(Quick Start)

Catch2

Catch2是及其簡單的C++測試框架,與gtest,boost.test和CppUnit相比Catch2很是小,甚至你只須要一個頭文件就能夠輕鬆的使用了。在小型項目裏面能夠很方便的用它搭建測試框架,同時配合簡單的打樁框架 stub,分分鐘讓你的測試用例跑起來。
今天,咱們就來【解鎖】Catch2。

獲取

有兩種方法獲取Catch2:
一種是直接下載頭文件catch.hpp——推薦使用這種方式,能夠簡單的融入你的項目。
另外一種是,獲取catch2源碼,https://github.com/catchorg/C... 適合二次開發或者學習裏面的demo。python

編譯

由於咱們今天要經過分析幾個Catch2的examples來解鎖Catch2的用法,因此用源碼進行編譯。c++

1.獲取源碼git

git clone https://github.com/catchorg/Catch2.git

2.開啓examples編譯
cmake -DCATCH_BUILD_EXAMPLES=ON ../github

(base) frank@deepin:~/git/Catch2$ mkdir build
(base) frank@deepin:~/git/Catch2$ cd build/
(base) frank@deepin:~/git/Catch2/build$ cmake -DCATCH_BUILD_EXAMPLES=ON ../
-- The CXX compiler identification is GNU 9.2.0
-- Check for working CXX compiler: /usr/local/bin/c++
-- Check for working CXX compiler: /usr/local/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found PythonInterp: /home/frank/miniconda3/bin/python (found version "3.7.4") 
-- Examples included
-- Configuring done
-- Generating done
-- Build files have been written to: /home/frank/git/Catch2/build
(base) frank@deepin:~/git/Catch2/build$ make -j4

測試

先從一個最簡單的例子開始。bash

#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 );
}

第1行:#define CATCH_CONFIG_MAIN ,這個宏定義了catch2的main函數。session

// Standard C/C++ main entry point
int main (int argc, char * argv[]) {
    return Catch::Session().run( argc, argv );
}

第3行:引入catch2的頭文件,這裏用的是"<...>",也就是使用編譯安裝的catch2,make後執行make install.安裝在系統目錄。若是用單個頭文件則應該使用"catch2.hpp",確認catch2.hpp在你的工程目錄或引入了其所在的頭文件目錄。app

第5行:int Factorial( int number ) 是被測函數。框架

第十、14行:分別是兩個測試用例
REQUIRE:是斷言。less

運行結果以下:ide

010-TestCase is a Catch v2.11.1 host application.
Run with -? for options

-------------------------------------------------------------------------------
Factorial of 0 is 1 (fail)
-------------------------------------------------------------------------------
/home/frank/git/Catch2/examples/010-TestCase.cpp:13
...............................................................................

/home/frank/git/Catch2/examples/010-TestCase.cpp:14: FAILED:
  REQUIRE( Factorial(0) == 1 )
with expansion:
  0 == 1

===============================================================================
test cases: 2 | 1 passed | 1 failed
assertions: 5 | 4 passed | 1 failed

本身寫main()

若是不想使用Catch2提供的main()函數,能夠本身編寫main()。每每不少項目須要本身寫main()函數,那麼可使用下面的方法。

#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;
}

若是想用本身的命令行參數,能夠這樣實現:

#define CATCH_CONFIG_RUNNER
#include "catch.hpp"

int main( int argc, char* argv[] )
{
  Catch::Session session; // There must be exactly one instance
  
  int height = 0; // Some user variable you want to be able to set
  
  // Build a new parser on top of Catch's
  using namespace Catch::clara;
  auto cli 
    = session.cli() // Get Catch's composite command line parser
    | Opt( height, "height" ) // bind variable to a new option, with a hint string
        ["-g"]["--height"]    // the option names it will respond to
        ("how high?");        // description string for the help output
        
  // Now pass the new composite back to Catch so it uses that
  session.cli( cli ); 
  
  // Let Catch (using Clara) parse the command line
  int returnCode = session.applyCommandLine( argc, argv );
  if( returnCode != 0 ) // Indicates a command line error
      return returnCode;

  // if set on the command line then 'height' is now set at this point
  if( height > 0 )
      std::cout << "height: " << height << std::endl;

  return session.run();
}

SECTION

你在測試某個類的時候須要對這個類總體屬性進行設置,能夠採用setup()和teardow()的方式。每一個成員方法的測試用例都基於這些特定的屬性,但每每每一個成員方法會有不一樣的測試場景,這時SECTION就能夠派上用場了。
也就是說,這個類某一組屬性的用例能夠是TEST_CASE的範疇,在這個TEST_CASE範疇內成員方法的不一樣場景能夠是SECTION範疇。
用下面的例子來講明:

#include <catch2/catch.hpp>

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

    // For each section, vector v is anew:

    std::vector<int> v( 5 );

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

    SECTION( "resizing bigger changes size and capacity" ) {
        v.resize( 10 );

        REQUIRE( v.size() == 10 );
        REQUIRE( v.capacity() >= 10 );
    }
    SECTION( "resizing smaller changes size but not capacity" ) {
        v.resize( 0 );

        REQUIRE( v.size() == 0 );
        REQUIRE( v.capacity() >= 5 );
    }
    SECTION( "reserving bigger changes capacity but not size" ) {
        v.reserve( 10 );

        REQUIRE( v.size() == 5 );
        REQUIRE( v.capacity() >= 10 );
    }
    SECTION( "reserving smaller does not change size or capacity" ) {
        v.reserve( 0 );

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

第7行:std::vector<int> v( 5 );是類(TEST_CASE)的範疇。
第13,19,25,31行:是resize()方法在不一樣場景的用例,是方法(SECTION)範疇。

這時你能夠用命令行參數來指定某一個SECTION執行。

(base) frank@deepin:~/git/Catch2/build/examples$ ./100-Fix-Section -c "resizing bigger changes size and capacity" 
===============================================================================
All tests passed (4 assertions in 1 test case)

BDD風格

整體上和SECTION相似,只不過更接近天然語言或行爲,BDD(行爲驅動開發)——你能夠把測試用例當作你的需求文檔。
例子:

#include <catch2/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() == 5 );
                REQUIRE( v.capacity() >= 5 );
            }
        }
    }
}
相關文章
相關標籤/搜索