📌 项目地址:catchorg/Catch2 | ⭐ 20,587 颗星 | 🔧 C++ | 📜 未标注
为什么需要 Catch2?
C++ 的测试框架不少,但大多要求你继承某个基类、用字符串宏定义测试名、断言语法像函数调用。Catch2 的设计思路很简单:让测试像正常 C++ 代码一样自然。测试名称不必是合法标识符(可以是任意字符串),断言直接用 REQUIRE(expr) 或 CHECK(expr) 这种接近布尔表达式的写法,而 SECTION 机制让你在同一个测试用例里共享 setup / teardown 代码,无需写额外的 fixture 类。
此外,Catch2 还内建了基本的微基准测试和BDD 风格的宏(SCENARIO、GIVEN、WHEN、THEN),一个框架覆盖了日常测试、性能回归、行为驱动开发三种场景。
实际用法:从单元测试到基准测试
1. 单元测试:自然断言 + SECTION
#include <catch2/catch_test_macros.hpp>
#include <cstdint>
uint32_t factorial( uint32_t number ) {
return number <= 1 ? number : factorial(number-1) * number;
}
TEST_CASE( "Factorials are computed", "[factorial]" ) {
REQUIRE( factorial(1) == 1 );
REQUIRE( factorial(2) == 2 );
REQUIRE( factorial(3) == 6 );
REQUIRE( factorial(10) == 3'628'800 );
}
TEST_CASE的第一个参数就是测试名称,可以是带空格、标点的任意字符串。- 第二个参数是一个标签(如
[factorial]),用于筛选运行哪些测试(例如命令行./tests "[factorial]")。 REQUIRE如果失败会立即终止当前测试用例,CHECK只记录错误不终止,适合收集多个失败。
2. Section:共享 setup / teardown 的优雅方式
TEST_CASE( "vectors can be sized and resized", "[vector]" ) {
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 都会从 TEST_CASE 的入口(构造向量)开始执行,但只执行该 SECTION 内部的代码,其余 SECTION 被跳过。这比 Google Test 的 SetUp/TearDown + 多个 TEST_F 更紧凑,避免了重复创建不同测试函数。
3. 微基准测试(内建)
#include <catch2/benchmark/catch_benchmark.hpp>
uint64_t fibonacci(uint64_t number) {
return number < 2 ? number : fibonacci(number - 1) + fibonacci(number - 2);
}
TEST_CASE("Benchmark Fibonacci", "[!benchmark]") {
REQUIRE(fibonacci(5) == 5);
REQUIRE(fibonacci(20) == 6'765);
BENCHMARK("fibonacci 20") {
return fibonacci(20);
};
REQUIRE(fibonacci(25) == 75'025);
BENCHMARK("fibonacci 25") {
return fibonacci(25);
};
}
- 基准测试默认不会运行,需要在命令行指定
[!benchmark]标签才能执行(例如./tests "[!benchmark]")。 BENCHMARK宏接受一个代码块,框架会自动多次执行并统计耗时,给出平均/最小/最大时间。- 基准测试与断言可以混用,适合先验证正确性再测性能。
与同类工具的区别
- vs Google Test:GTest 需要写
TEST()或TEST_F(),测试名是标识符;Catch2 的测试名可以是任意字符串。GTest 的ASSERT_TRUE/EXPECT_TRUE不如 Catch2 的REQUIRE(expr)自然。Catch2 的SECTION机制在 fixture 组织上更轻量,无需继承。Catch2 内建基准测试,GTest 没有。 - vs boost.test:boost.test 宏更冗长,需要
BOOST_AUTO_TEST_CASE,且 boost 库体积大。Catch2 对现代 C++(C++14/17/20)支持更好,编译速度也更快(单头文件时期优势明显,v3 虽然改为分离库,但仍保持模块化)。 - vs doctest:doctest 是 Catch2 的轻量化变种,编译速度更快,但功能(benchmark、BDD)不如 Catch2 完整。
需要注意的事项
-
Catch2 v3 不再是一个单头文件库
v3 版本需要将src/catch2.cpp编译成单独的库文件,然后链接。迁移指南参考 docs/migrate-v2-to-v3.md。如果仍想用单头文件,可以使用v2.x分支(支持 C++11)。 -
基准测试并非默认运行
必须显式使用[!benchmark]标签,否则框架会跳过基准测试块。 -
依赖
Catch2 v3 需要 C++14 或更高版本。C++11 用户请使用v2.x分支。 -
文档尚在更新
v3 版本的文档(尤其是安装和构建部分)还在完善中,如有疑问可查阅 GitHub Issues 或加入 Discord 社区。 -
不完全兼容 v2
v3 改动了头文件组织方式和部分宏名称,迁移时需要修改 include 路径和少量代码。
总结
Catch2 适合那些希望用最少的样板代码写出可读性高、维护成本低的 C++ 测试的团队。它的自然断言、Section、标签系统、内建基准测试和 BDD 支持,让一个框架覆盖了大多数日常测试需求。如果项目已经使用 C++14 或更新版本,并且不希望依赖 Google Test 或 Boost 那样庞大的依赖,Catch2 值得投入。