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