1.简介
catch是一个C++多范式测试框架,以头文件形式提供支持
主要特性:
- 头文件方式支持,无其他库依赖
- 支持自注册功能/函数
- 支持BDD-style
- 测试用例相互隔离
- 支持标准C++比较操作符
- 支持标记分组
- 可测试自己
2.使用
2.1 基本
TEST_CASE( test name [tags] )
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
TEST_CASE( "vectors can be sized and resized", "[vector]") {
std::vector<int> v( 5 );
REQUIRE( v.size() == 5 );
REQUIRE( v.capacity() >= 5 );
}
支持自定义main,添加 CATCH_CONFIG_MAIN 宏即可
如果需要使用自定义的main函数,添加CATCH_CONFIG_RUNNER 宏,在完成必要的逻辑后,添加 return Catch::Session().run(argc, argv);
2.1.1 断言宏
REQUIRE( expression )
CHECK( expression )
两者区别:CHECK 失败后继续执行当前test case
针对 CHECK(a == 1 && b == 2) 情况,需要在表达式两边添加() => CHECK((a == 1 && b == 2)),否则会编译不通过
2.1.2 浮点对比
catch 利用 Approx 类用于浮点数的判断
Approx 重载了比较操作符,可直接使用比较操作符,进行判断。
Approx 提供了3种方法:
- epsilon 相对于原值的百分比值偏差,比如epsilon=0.01,表示与其比较的值在Approx值的%1的范围均为相等
- margin 绝对偏差数值
- scale 偏差范围:(Approx::scale + Approx::value)* epsilon,scale对margin无影响
//epsilon
Approx targete = Approx(100).epsilon(0.01);
REQUIRE(100.0 == targete); //true
REQUIRE(200.0 == targete); //false
//margin
Approx targetm = Approx(100).margin(5);
REQUIRE(100.0 == targetm); //true
REQUIRE(200.0 == targetm); //false
//scale
Approx targets = Approx(100).scale(100).epsilon(0.01);
REQUIRE(100.7 == targets); // true
2.1.3 Matchers
执行类型是否和Matcher所定义的条件match,目前支持string、vector、Floating point、Generic、Exception Maters,支持自定义matcher。
通过REQUIRE_THAT() CHECK_THAT() 宏引入,支持2个参数,第一个参数为被测试对象/值,第二个为matcher表达式,支持多个表达式 &&, || or !
详细方法:https://github.com/catchorg/Catch2/blob/master/docs/matchers.md#top
Demo
TEST_CASE( "matcher test", "[hello]" ) {
REQUIRE_THAT( str,
EndsWith( "hello world" ) ||
(StartsWith( "Daniel" ) && !Contains( "very good" ) ) );
}
自定义Matchers,继承Catch::MatcherBase<T>,实现match()、describe()函数即可
class IntRange : public Catch::MatcherBase<int> {
int m_begin, m_end;
public:
IntRange( int begin, int end ) : m_begin( begin ), m_end( end ) {}
bool match( int const& i ) const override {
return i >= m_begin && i <= m_end;
}
virtual std::string describe() const override {
std::ostringstream ss;
ss << "is between " << m_begin << " and " << m_end;
return ss.str();
}
};
inline IntRange IsBetween( int begin, int end ) {
return IntRange( begin, end );
}
// Usage
TEST_CASE("Integers are within a range")
{
CHECK_THAT( 3, IsBetween( 1, 10 ) );
CHECK_THAT( 100, IsBetween( 1, 10 ) );
}
2.2 section
Test case支持多个sections的创建,sections前后可进行setup teardown管理。每个section都是独立的单元,每个section都是从test case的最开始运行,section串行执行。支持嵌套section,当前section执行失败后,嵌套的section将不会执行
Demo
TEST_CASE( "vectors sized and resized", "[vector]" ) {
//setup
std::vector<int> v( 5 );
REQUIRE( v.size() == 5 );
REQUIRE( v.capacity() >= 5 );
SECTION( "resizing size and capacity" ) {
v.resize( 10 );
REQUIRE( v.size() == 10 );
REQUIRE( v.capacity() >= 10 );
}
SECTION( "resizing size but not capacity" ) {
v.resize( 0 );
REQUIRE( v.size() == 0 );
REQUIRE( v.capacity() >= 5 );
SECTION( "reserving again does not change capacity" ) {
v.reserve( 7 );
REQUIRE( v.capacity() >= 10 );
}
}
//teardown
}
2.3 BDD-style
BDD-行为驱动开发
Demo
SCENARIO( "vectors sized and resized", "[vector]" ) {
GIVEN( "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( "size changes but not capacity" ) {
REQUIRE( v.size() == 0 );
REQUIRE( v.capacity() >= 5 );
}
}
}
}
3.其他
3.1 日志
- UNSCOPED -- 临时,保留check false最新前一条日志,其余被覆盖, 成功的 case 不打印日志
- INFO -- 成功的 case 不打印日志
- WARN -- 一直显示,不中断 case
- FAIL -- 发生时,中断 case
- FAIL_CHECK -- 同fail,不中断 case
支持streaming流式操作
WARN/WARN/FAIL_CHECK发生时,INFO日志不打印
3.2 Test fixtures
- TEST_CASE_METHOD 提供通过继承方式,实现对私有/保护函数的测试
demo
class DBConnection {
public:
bool executeSQL(const std::string& str, const int& str1, const std::string& str2){
return true;
}
};
class UniqueTestsFixture {
private:
static int uniqueID;
protected:
DBConnection conn;
protected:
int getID() {
return ++uniqueID;
}
};
int UniqueTestsFixture::uniqueID = 0;
TEST_CASE_METHOD(UniqueTestsFixture, "Create Employee/No Name", "[hello]") {
REQUIRE_THROWS(conn.executeSQL("INSERT INTO employee (id, name) VALUES (?, ?)", getID(), ""));
}
TEST_CASE_METHOD(UniqueTestsFixture, "Create Employee/Normal", "[world]") {
REQUIRE(conn.executeSQL("INSERT INTO employee (id, name) VALUES (?, ?)", getID(), "Joe Bloggs"));
}
模板测试,要求tag非空,如果有多个模板参数,需要括号包围
- TEMPLATE_TEST_CASE_METHOD 模板测试
格式:TEMPLATE_TEST_CASE/_METHOD ( test name , tags, type1, type2, ..., typen )
- TEMPLATE_PRODUCT_TEST_CASE_METHOD 模板模板测试
格式:TEMPLATE_PRODUCT_TEST_CASE/_METHOD ( test name , tags, (template-type1, template-type2, ..., template-typen), (template-arg1, template-arg2, ..., template-argm) )
链接:
https://github.com/catchorg/Catch2/blob/67b4ada6b0fbe98368df934e1378aeae1ba7f235/docs/test-fixtures.md
https://github.com/catchorg/Catch2/blob/67b4ada6b0fbe98368df934e1378aeae1ba7f235/docs/test-cases-and-sections.md
3.3 Event Listeners
通过继承Catch::TestEventListenerBase,以实现用例/section/Assertions的hook
4.备注
- 多Test文件情况下,CATCH_CONFIG_MAIN 仅需定义一次
- github地址:https://github.com/catchorg/Catch2