必须先定义 catch_config_enable_bdd_macros 再包含 catch2/catch_test_macros.hpp,否则 scenario/given/when/then 宏无效;变量需声明在 scenario 顶层,断言必须用 require/check 而非 assert。

如何用 Catch2 的单头文件方式启动 BDD 风格测试
Catch2 支持直接 #include Catch2/catch_test_macros.hpp(或旧版 catch.hpp)完成零构建依赖的测试启动,但默认启用的是 TDD 风格(TEST_CASE)。要写 BDD 风格(Given-When-Then),必须显式启用 CATCH_CONFIG_ENABLE_BDD_MACROS 宏,且必须在包含头文件之前定义。
常见错误是把 #define CATCH_CONFIG_ENABLE_BDD_MACROS 放在 #include 之后,或放在测试文件中间——这时所有 SCENARIO、GIVEN 等宏都会被忽略,编译器报错说未声明。
- 正确顺序:先
#define CATCH_CONFIG_ENABLE_BDD_MACROS,再#include <catch2></catch2> - 如果使用 CMake,推荐改用
find_package(Catch2 REQUIRED)+catch_discover_tests(),避免手动管理宏定义顺序 - 单头文件模式下不建议同时定义
CATCH_CONFIG_MAIN多次;多个测试文件时,只在一个文件里定义它,其余只包含头文件
SCENARIO / GIVEN / WHEN / THEN 宏的实际行为和嵌套限制
这些不是语法关键字,而是预处理宏,最终都展开为 SECTION 或带描述的 TEST_CASE。它们不构成真正的代码块作用域:变量不能跨 GIVEN/WHEN 声明,也不能靠缩进隐式嵌套。
例如下面写法是错的:
SCENARIO("user login") {
GIVEN("a valid user") {
auto user = User{"alice", "pass123"}; // ← 这个变量在 WHEN 里不可见
}
WHEN("they submit credentials") {
// user 未定义!
}
}
- 所有变量需声明在
SCENARIO顶层作用域,或用static/ 捕获 lambda 模拟状态传递 -
AND_WHEN和THEN是纯文档宏,不改变执行逻辑,仅影响报告输出格式 - 嵌套
SECTION才真正产生组合测试分支;BDD 宏只是语义糖,不会自动展开多路径
让 BDD 测试真正轻量:禁用不需要的特性
Catch2 默认开启颜色输出、堆栈跟踪、异常捕获等,对嵌入式或 CI 环境可能造成开销或干扰。BDD 测试若只跑验证逻辑,可精简:
- 加
#define CATCH_CONFIG_NO_COLOUR关闭 ANSI 颜色,避免终端兼容问题 - 加
#define CATCH_CONFIG_DISABLE_EXCEPTIONS后,REQUIRE会转为std::abort(),无异常开销(但失去断言上下文) - 不用
INFO或CAPTURE宏时,可定义CATCH_CONFIG_DISABLE_MATCHERS减小二进制体积 - 单头文件编译慢?用
#pragma once+ 前置声明隔离测试文件,或把公共 fixture 提到单独头中避免重复解析
为什么 assert(false) 在 WHEN 里不触发失败报告
这是最常被忽略的陷阱:WHEN 宏本身不捕获异常或检查返回值,它只是给测试段落加标签。如果你在 WHEN 块里写 assert(false),程序会直接 abort,Catch2 来不及记录失败位置,报告里只显示“test case crashed”。
正确做法始终用 Catch2 自带的断言:
- 用
REQUIRE(…)替代assert(…)—— 它会记录表达式、文件行号,并支持自定义字符串消息 - 需要检查异常时,必须用
REQUIRE_THROWS_AS(expr, std::runtime_error),不能靠 try/catch + assert 模拟 -
THEN块里也一样:它不提供额外保护,只是语义提示,断言仍需显式写出
BDD 标签的价值在于可读性与协作沟通,不是运行时保障;所有校验逻辑必须落在 REQUIRE/CHECK 上,否则就只是注释。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











