catch2 是头文件库,下载 catch2/catch_all.hpp 并 #include;多测试文件时仅一个定义 catch_config_main;test_case 为宏不可嵌套;require 失败终止,check 继续执行;ci 需 --no-color、--reporter junit 等参数;断言宏单次求值防副作用。

怎么在 C++ 项目里加 Catch2 测试
直接把 Catch2 加进项目,不是装个库就完事——它没编译期依赖,也不需要链接静态库,本质是个「头文件即库」的方案。最轻量的做法是下载单头文件 catch2/catch_all.hpp 放进项目目录(比如 test/include/),然后写测试文件 #include 它就行。
常见错误现象:编译报 undefined reference to 'main'——因为 Catch2 默认自己提供 main(),但你如果已有 main(),就得关掉它:
- 定义宏
CATCH_CONFIG_RUNNER再 #include,自己写main()并调用Catch::Session().run(argc, argv) - 或者更简单:只在**唯一一个测试文件**里定义
CATCH_CONFIG_MAIN,其他测试文件只 #include 不定义宏 - 别在多个 .cpp 里都定义
CATCH_CONFIG_MAIN,否则链接重复main
Catch2 的 TEST_CASE 怎么写才不踩坑
TEST_CASE 是最常用入口,但它不是函数,而是宏展开成匿名 lambda + 注册逻辑。这意味着:
- 不能在函数内嵌套写
TEST_CASE;也不能把它当普通作用域用——变量不自动销毁,多次运行时状态可能残留 - 想隔离状态?用
SECTION或拆成多个TEST_CASE,别靠缩进或花括号“假装”作用域 - 参数化测试别硬写循环,用
TEMPLATE_TEST_CASE或TEST_CASE_METHOD配合 fixture,否则断言失败时堆栈难定位 -
REQUIRE和CHECK区别很关键:REQUIRE失败直接跳出当前测试用例;CHECK记录失败继续跑,适合检查多个不强相关的点
示例(正确用法):
立即学习“C++免费学习笔记(深入)”;
TEST_CASE("vector size after push") {
std::vector<int> v;
v.push_back(42);
REQUIRE(v.size() == 1); // 推荐:核心前提必须成立
CHECK(v[0] == 42); // 补充检查,不影响后续断言
}
CI 里跑 Catch2 测试总失败?看这几个配置点
Catch2 默认输出是彩色控制台文本,CI 环境常禁用颜色、限制超时、要求 XML 报告——这些全靠命令行参数控制,不是改代码。
- 关颜色:
--no-color,否则某些 CI 日志解析器会把 ANSI 转义符当乱码报错 - 生成 JUnit 兼容 XML:
--reporter junit --out test-report.xml,注意--out必须紧跟--reporter后面,顺序错就无效 - 避免卡死:加
--timeout 30(单位秒),Catch2 会为每个TEST_CASE单独设超时 - 过滤运行:用
-t "network"只跑打了[network]标签的用例,配合TEST_CASE("http client", "[network]")
为什么 assert 里用 REQUIRE 而不是 if + std::abort
因为 REQUIRE 不只是终止,它会捕获表达式字符串、文件名、行号、实际值和期望值——这些信息在调试时比 std::abort() 强太多。
- 写
if (!ptr) std::abort();:崩溃了,但不知道 ptr 是啥、哪一行、为什么空 - 写
REQUIRE(ptr != nullptr);:输出类似FAILED: ptr != nullptr,附带文件、行号、ptr的实际地址 - 自定义类型要支持打印?重载
operator 到 <code>std::ostream&,Catch2 会自动用它格式化值 - 性能影响极小:宏在编译期展开,失败时才有额外字符串拼接开销,日常开发完全可忽略
真正容易被忽略的是:Catch2 的断言宏对表达式求值**仅一次**,而手写 if (exp) abort() 如果 exp 有副作用(比如 func()),重复求值会导致行为不一致——这点连老手都偶尔翻车。










