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
若是不想使用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(); }
你在測試某個類的時候須要對這個類總體屬性進行設置,能夠採用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)
整體上和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 ); } } } }