Catch2
Catch2是及其簡(jiǎn)單的C++測(cè)試框架擎淤,與gtest,boost.test和CppUnit相比Catch2非常小,甚至你只需要一個(gè)頭文件就可以輕松的使用了。在小型項(xiàng)目里面可以很方便的用它搭建測(cè)試框架捧杉,同時(shí)配合一個(gè)更為簡(jiǎn)單的打樁框架stub陕见,分分鐘讓你的測(cè)試用例跑起來(lái)。
今天味抖,我們就來(lái)【解鎖】Catch2评甜。
獲取
有兩種方法獲取Catch2:
一種是直接下載頭文件catch.hpp——推薦使用這種方式,可以簡(jiǎn)單的融入你的項(xiàng)目仔涩。
另一種是忍坷,獲取catch2源碼,https://github.com/catchorg/Catch2.git 適合二次開(kāi)發(fā)或者學(xué)習(xí)里面的demo熔脂。
編譯
因?yàn)槲覀兘裉煲ㄟ^(guò)分析幾個(gè)Catch2的examples來(lái)解鎖Catch2的用法佩研,所以用源碼進(jìn)行編譯。
- 獲取源碼
git clone https://github.com/catchorg/Catch2.git
- 開(kāi)啟examples編譯
(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
測(cè)試
先從一個(gè)最簡(jiǎn)單的例子開(kāi)始锤悄。
#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 韧骗,這個(gè)宏定義了catch的main函數(shù)。
// Standard C/C++ main entry point
int main (int argc, char * argv[]) {
return Catch::Session().run( argc, argv );
}
第3行:引入catch2零聚,頭文件,這里用的是"<...>"些侍,也就是使用編譯安裝的catch2隶症,make后執(zhí)行make install.咋裝在系統(tǒng)目錄。如果用單個(gè)頭文件則應(yīng)該使用"catch2.hpp"岗宣,確認(rèn)catch2.hpp
在你的攻寵目錄活引入了其所在的頭文件目錄蚂会。
第5行:int Factorial( int number ) 是被測(cè)函數(shù)。
第10耗式、14行:分別是兩個(gè)測(cè)試用例
REQUIRE:是斷言胁住。
運(yùn)行結(jié)果如下:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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
自己寫(xiě)main()
如果不想使用Catch2提供的main()函數(shù),往往很多項(xiàng)目需要自己寫(xiě)main()函數(shù)刊咳,那么可以使用下面的方法彪见。
#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;
}
如果想用自己的命令行參數(shù),可以這樣實(shí)現(xiàn):
#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
你在測(cè)試某個(gè)類的時(shí)候需要對(duì)這個(gè)類整體屬性進(jìn)行設(shè)置娱挨,可以采用setup()活teardow()的方式余指。每個(gè)方法都基于這些特定的屬性,但往往每個(gè)成員方法有需要有不同的測(cè)試場(chǎng)景跷坝,這時(shí)SECTION就可以派上用場(chǎng)酵镜。
也就是說(shuō),這個(gè)類某一組屬性的用例可以是TEST_CASE的范疇柴钻,在這個(gè)TEST_CASE范疇內(nèi)成員方法的不同場(chǎng)景可以使SECTION范疇淮韭。
用下面的例子來(lái)說(shuō)明:
#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()
方法不同場(chǎng)景的測(cè)試贴届,是方法(SECTION)范疇靠粪。
這時(shí)你可以用命令行參數(shù)來(lái)指定某一個(gè)SECTION執(zhí)行蜡吧。
(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風(fēng)格
總體上和SECTION類似,只不過(guò)更接近自然語(yǔ)言或行為庇配,BDD(行為驅(qū)動(dòng)開(kāi)發(fā))——你可以把測(cè)試用例當(dāng)做你的需求文檔斩跌。
例子:
#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 );
}
}
}
}