Catch2 CMake 集成完全指南:从链接 Target、自动注册测试到分片与安装
【免费下载链接】Catch2A modern, C++-native, test framework for unit-tests, TDD and BDD - using C++14, C++17 and later (C++11 support is in v2.x branch, and C++03 on the Catch1.x branch)项目地址: https://gitcode.com/GitHub_Trending/ca/Catch2
Catch2 本身就是用 CMake 构建的,因此它为使用方提供了两条标准集成通道:导出的(namespaced)CMake Target,以及位于extras目录中用于把TEST_CASE自动注册进 CTest 的 CMake 脚本。本文基于 docs/cmake-integration.md 展开,结合仓库中 CMakeLists.txt、src/CMakeLists.txt、extras/Catch.cmake 等源码实现,系统讲解如何在自己的 CMake 工程中链接 Catch2、自动发现测试、按需分片,以及通过 CMake 配置项、CATCH_CONFIG_*开关和多种安装方式接入 Catch2,读完后可直接照搬到实际项目中。
CMake Targets:Catch2::Catch2与Catch2::Catch2WithMain
Catch2 的 CMake 构建会导出两个 target:Catch2::Catch2和Catch2::Catch2WithMain。
Catch2::Catch2WithMain:如果你的测试代码不需要自定义main函数,应当使用它(且只用它)。链接它会自动添加正确的 include 路径,并把你的目标与两个静态库链接到一起——一个实现 Catch2 本体,一个实现其main入口。Catch2::Catch2:如果你需要自定义main(例如自己解析命令行参数、注入自定义 CLI 选项),则只链接Catch2::Catch2。
在系统已安装 Catch2 的前提下,最小用法如下:
find_package(Catch2 3 REQUIRED) # 这些测试使用 Catch2 提供的 main add_executable(tests test.cpp) target_link_libraries(tests PRIVATE Catch2::Catch2WithMain) # 这些测试需要自己的 main add_executable(custom-main-tests test.cpp test-main.cpp) target_link_libraries(custom-main-tests PRIVATE Catch2::Catch2)以子目录方式使用(add_subdirectory)
当 Catch2 以子目录形式被引入时,这两个 target 同样可用。假设 Catch2 已克隆到lib/Catch2,只需把上面的find_package调用替换为add_subdirectory(lib/Catch2),其余代码原样照搬即可。仓库 examples/CMakeLists.txt 中的示例正是这样做的:它把所有示例目标与Catch2WithMain链接,从而省去手写main。
需要自定义main时,可以参考 examples/232-Cfg-CustomMain.cpp:它创建唯一的Catch::Session实例,基于Catch::Clara在 Catch2 既有命令行解析器之上追加--height之类的自定义选项,再交给session.applyCommandLine处理,最后调用session.run()。
使用 FetchContent 拉取
如果你不希望把 Catch2 提交进自己的仓库,可以使用 CMake 的 FetchContent:
Include(FetchContent) FetchContent_Declare( Catch2 GIT_REPOSITORY https://gitcode.com/GitHub_Trending/ca/Catch2.git GIT_TAG v3.15.2 # 或更新的 release(当前仓库版本为 3.15.2) ) FetchContent_MakeAvailable(Catch2) add_executable(tests test.cpp) target_link_libraries(tests PRIVATE Catch2::Catch2WithMain)Target 的底层导出实现
这两个 target 的生成与导出逻辑位于 src/CMakeLists.txt:
Catch2库由 src/catch2 下的全部实现文件(IMPL_SOURCES、INTERFACE_SOURCES、REPORTER_SOURCES、MATCHER_SOURCES、GENERATOR_SOURCES、BENCHMARK_SOURCES)编译而成,并通过add_library(Catch2::Catch2 ALIAS Catch2)提供命名空间别名;- 它要求 C++14(
target_compile_features(Catch2 PUBLIC cxx_std_14)),并通过$<BUILD_INTERFACE:...>/$<INSTALL_INTERFACE:...>区分构建期与安装期的 include 路径; Catch2WithMain单独编译 src/catch2/internal/catch_main.cpp,输出名被设为Catch2Main,并target_link_libraries(Catch2WithMain PUBLIC Catch2),这正是“链接 WithMain 即自动带上实现库”的原因;- 安装时,两者被打包进
Catch2Targets导出集合,并冠以Catch2::命名空间安装到${CMAKE_INSTALL_LIBDIR}/cmake/Catch2。
安装后供find_package使用的配置文件是 CMake/Catch2Config.cmake.in:它先检查Catch2::Catch2目标是否已存在以避免重复引入,然后向CMAKE_MODULE_PATH追加自身所在目录(这样include(Catch)等脚本也能被找到),最后include(Catch2Targets.cmake)。此外,顶层 CMakeLists.txt 还会在安装时生成catch2.pc与catch2-with-main.pc(模板见 CMake/catch2.pc.in 与 CMake/catch2-with-main.pc.in),为使用 pkg-config 的项目提供同样的链接信息。
自动测试注册:把TEST_CASE变成 CTest 用例
Catch2 仓库的extras目录提供了三套辅助脚本:
Catch.cmake(及其依赖CatchAddTests.cmake)——推荐方案;ParseAndAddCatchTests.cmake(已弃用);CatchShardTests.cmake(及其依赖CatchShardTestsImpl.cmake)。
如果 Catch2 已安装到系统,执行find_package(Catch2 REQUIRED)之后即可直接include这些脚本;否则需要手动把extras目录加入CMAKE_MODULE_PATH。
catch_discover_tests:运行期枚举测试
Catch.cmake提供函数catch_discover_tests。它的原理是运行编译好的测试可执行文件,传入--list-tests --reporter json --out <临时文件>,再把 JSON 输出解析为一个个独立的 CTest 测试。从 extras/CatchAddTests.cmake 的实现可以看到,它要求输出 JSON 的version字段为"1",随后逐条读取测试名、可选的 tags,并为每个测试生成add_test与set_tests_properties写入 CTest 脚本。由于发现过程发生在构建/测试阶段,新增或删除测试无需重新运行 CMake 配置。
基本用法:
cmake_minimum_required(VERSION 3.16) project(baz LANGUAGES CXX VERSION 0.0.1) find_package(Catch2 REQUIRED) add_executable(tests test.cpp) target_link_libraries(tests PRIVATE Catch2::Catch2) include(CTest) include(Catch) catch_discover_tests(tests)使用 FetchContent 时的注意点
使用 FetchContent 时,include(Catch)会失败,除非显式把extras目录加入CMAKE_MODULE_PATH:
# ... FetchContent ... # list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras) include(CTest) include(Catch) catch_discover_tests(tests)完整参数列表
catch_discover_tests(target [TEST_SPEC arg1...] [EXTRA_ARGS arg1...] [WORKING_DIRECTORY dir] [TEST_PREFIX prefix] [TEST_SUFFIX suffix] [PROPERTIES name1 value1...] [TEST_LIST var] [REPORTER reporter] [OUTPUT_DIR dir] [OUTPUT_PREFIX prefix] [OUTPUT_SUFFIX suffix] [DISCOVERY_MODE <POST_BUILD|PRE_TEST>] [SKIP_IS_FAILURE] [ADD_TAGS_AS_LABELS] [DL_PATHS path...] [DL_FRAMEWORK_PATHS path...] )其中DL_PATHS与DL_FRAMEWORK_PATHS在 extras/Catch.cmake 中还有进一步支持(分别对应 Linux/macOS/Windows 的LD_LIBRARY_PATH/DYLD_LIBRARY_PATH/PATH与 macOS 的DYLD_FRAMEWORK_PATH,要求 CMake ≥ 3.22)。各参数含义:
TEST_SPEC arg1...:指定要传给测试可执行文件的测试用例、通配用例、标签或标签表达式,与--list-test-names-only一起使用,实现只发现子集测试。EXTRA_ARGS arg1...:运行每个测试时额外追加的命令行参数。WORKING_DIRECTORY dir:运行已发现测试的目录,缺省为当前二进制目录。TEST_PREFIX prefix:为每个发现的测试名添加前缀。当同一个测试可执行文件被多次调用catch_discover_tests()且使用不同TEST_SPEC/EXTRA_ARGS时非常有用。TEST_SUFFIX suffix:与TEST_PREFIX相对,为测试名追加后缀,两者可同时使用。PROPERTIES name1 value1...:为本次调用发现的所有测试设置额外属性。TEST_LIST var:把测试列表保存到变量var而非默认的<target>_TESTS,便于同一可执行文件被多次发现时区分;注意该变量只在 CTest 运行期可用。REPORTER reporter:使用指定 reporter 运行测试,最终以--reporter reporter传给测试程序。OUTPUT_DIR dir:以--out dir/<test_name>形式传给可执行文件,文件名与测试名一致。应优先用它而不是EXTRA_ARGS --out foo,以避免并行执行时写同一输出文件的竞争条件。OUTPUT_PREFIX prefix:与OUTPUT_DIR联用,得到--out dir/prefix<test_name>。OUTPUT_SUFFIX suffix:与OUTPUT_DIR联用,得到--out dir/<test_name>suffix,可用于补充扩展名(如.xml)。DISCOVERY_MODE mode:控制测试发现时机。POST_BUILD(默认)在构建时发现;PRE_TEST推迟到测试执行前(适用于交叉编译等场景)。未传参时取CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE变量的值,实现全局统一行为。注意:在 Apple Silicon + Xcode 生成器下必须使用PRE_TEST,否则默认的POST_BUILD会因 macOS 拒绝运行未签名二进制而报Result: Subprocess killed——Xcode 只在 post-build 脚本之后才对测试可执行文件签名。SKIP_IS_FAILURE:让被跳过的测试按失败处理。默认情况下,extras/Catch.cmake 会给测试附加SKIP_RETURN_CODE 4(Catch2 约定跳过返回码),以实现 CTest 对跳过状态的识别。ADD_TAGS_AS_LABELS:把测试的 tags 同时作为 CTest 标签(labels)添加。实现上,extras/CatchAddTests.cmake 会解析 JSON 中的tags数组,并对含分号的标签做\;转义后写入LABELS属性。DL_PATHS path.../DL_FRAMEWORK_PATHS path...:设置测试执行时动态链接器查找共享库/DLL 的路径(分别写入LD_LIBRARY_PATH/PATH与DYLD_FRAMEWORK_PATH),在发现测试和真正执行测试时都会生效。
仓库自带的使用实例位于 tests/TestScripts/DiscoverTests/CMakeLists.txt:它以add_subdirectory引入 Catch2,链接Catch2::Catch2WithMain,并同时使用了ADD_TAGS_AS_LABELS、DISCOVERY_MODE PRE_TEST,且在 CMake ≥ 3.27 时追加DL_PATHS。
版本提示:
catch_discover_tests内部依赖 CMake 的 JSON 字符串解析能力,extras/Catch.cmake 中明确要求 CMake 版本 ≥ 3.19,否则会以FATAL_ERROR终止。虽然入门示例只写了cmake_minimum_required(VERSION 3.16),实际使用该函数时请确保 CMake 版本满足此要求。
ParseAndAddCatchTests(已弃用)
⚠ 该脚本在 Catch2 2.13.4 起被标记为弃用,由上文
catch_discover_tests方案取代。
它的工作方式与运行期发现截然不同:静态解析目标关联的所有实现文件,再通过 CTest 的add_test注册测试。这种方案有固有缺陷:被注释掉的测试也会被注册;而且它只能识别断言宏的一个子集,任何无法解析出宏的测试会被静默忽略。
用法:
cmake_minimum_required(VERSION 3.16) project(baz LANGUAGES CXX VERSION 0.0.1) find_package(Catch2 REQUIRED) add_executable(tests test.cpp) target_link_libraries(tests PRIVATE Catch2::Catch2) include(CTest) include(ParseAndAddCatchTests) ParseAndAddCatchTests(tests)自定义点(均为变量,默认值见下表):
| 变量 | 作用 | 默认值 |
|---|---|---|
PARSE_CATCH_TESTS_VERBOSE | ON时打印调试信息 | OFF |
PARSE_CATCH_TESTS_NO_HIDDEN_TESTS | ON时不注册隐藏测试(带[.]或[.foo]标签) | OFF |
PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME | ON时把 fixture 类名加入 CTest 测试名 | ON |
PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME | ON时把 target 名加入 CTest 测试名 | ON |
PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS | ON时把测试文件加入CMAKE_CONFIGURE_DEPENDS,测试文件变化会触发重新 configure 以自动发现新测试 | OFF |
还可在调用前设置OptionalCatchTestLauncher来包装启动命令,例如让部分测试通过 MPI 运行:
set(OptionalCatchTestLauncher ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${NUMPROC}) ParseAndAddCatchTests(mpi_foo) unset(OptionalCatchTestLauncher) ParseAndAddCatchTests(bar)catch_add_sharded_tests:把测试拆成随机分片
CatchShardTests.cmake自 Catch2 3.1.0 引入。
catch_add_sharded_tests(TEST_BINARY)把TEST_BINARY的测试拆分到多个分片(shard)中。每个分片内测试的内容与顺序是随机化的,种子每次调用 CTest 都会变化——实现上,extras/CatchShardTestsImpl.cmake 在生成的 CTest 脚本里先用string(RANDOM ...)生成 8 位十六进制种子,再为每个分片注册add_test(<target>-shard-<i>/<n> <binary> --shard-index <i> --shard-count <n> --rng-seed 0x<seed> --order rand ...),从而让每次 CTest 运行的测试分布都不同。
目前支持三个自定义点:
SHARD_COUNT:分片数量。未指定时,extras/CatchShardTests.cmake 中默认值为2。REPORTER:测试使用的 reporter 规格。TEST_SPEC:用于过滤测试的测试规格。
示例:
include(CatchShardTests) catch_add_sharded_tests(foo-tests SHARD_COUNT 4 REPORTER "xml::out=-" TEST_SPEC "A" ) catch_add_sharded_tests(tests SHARD_COUNT 8 REPORTER "xml::out=-" TEST_SPEC "B" )上述配置共注册 12 个 CTest 测试(4 + 8 个分片),各自从对应测试二进制中按 test spec 过滤后运行。仓库中 tests/TestScripts/testSharding.py 与 tests/TestScripts/testBazelSharding.py 分别验证了分片前后测试集合的一致性,以及 Bazel 环境下分片相关环境变量的行为。
注意:该脚本目前是“每次 CTest 运行重新播种分片”的概念验证实现,因此不支持(当前也不打算支持)
catch_discover_tests的全部自定义点。
CMake 工程选项
作为可被消费的 CMake 工程,Catch2 提供了若干选项(定义于顶层 CMakeLists.txt):
| 选项 | 作用 | 默认值 |
|---|---|---|
BUILD_TESTING | ON且不是作为子工程使用时,构建 Catch2 测试二进制 | ON |
CATCH_INSTALL_DOCS | ON时把文档安装到系统 | ON |
CATCH_INSTALL_EXTRAS | ON时把extras(上述 CMake 脚本、调试器辅助文件)一并安装 | ON |
CATCH_DEVELOPMENT_BUILD | ON时按“开发 Catch2 本身”配置构建(启用测试工程、警告等) | OFF |
CATCH_ENABLE_REPRODUCIBLE_BUILD | ON时为构建添加可复现性编译参数 | ON |
开启CATCH_DEVELOPMENT_BUILD后还会解锁一组开发用选项:
CATCH_BUILD_TESTING:构建 SelfTest 工程,默认ON。注意 Catch2 同时遵守标准BUILD_TESTING变量,两者都需为ON才会构建 SelfTest,任意一个设为OFF都能禁用。CATCH_BUILD_EXAMPLES:构建 examples 下的用法示例,默认OFF。CATCH_BUILD_EXTRA_TESTS:构建 tests/ExtraTests 额外测试,默认OFF。CATCH_BUILD_FUZZERS:构建 fuzzing 模糊测试入口,默认OFF。CATCH_ENABLE_WERROR:为编译添加-Werror或等价标志,默认ON。CATCH_BUILD_SURROGATES:ON时逐个独立编译 Catch2 的每个头文件(生成“代理翻译单元”),以验证它们自给自足,默认OFF。
从源码结构看,顶层 CMakeLists.txt 还通过cmake_dependent_option声明了CATCH_BUILD_BENCHMARKS(构建 benchmarks)、CATCH_ENABLE_COVERAGE(生成覆盖率)、CATCH_ENABLE_CONFIGURE_TESTS与CATCH_ENABLE_CMAKE_HELPER_TESTS(均为“非常昂贵”的 CMake 自身测试,默认OFF)等更多选项,开发 Catch2 时可按需开启。
另外,Catch2 不支持 in-tree 构建:当CMAKE_BINARY_DIR与源码目录相同时会直接FATAL_ERROR,请始终使用独立构建目录。
在 CMake 中定制CATCH_CONFIG_*编译期选项
CMake 对
CATCH_CONFIG_*选项的支持自 Catch2 3.0.1 引入。
由于 Catch2 v3 采用新的分离编译模型,docs/configuration.md 中列出的所有编译期配置项都可以通过 CMake 设置:把对应选项定义为ON即可,例如-DCATCH_CONFIG_NOSTDOUT=ON。
这些选项在 CMake/CatchConfigOptions.cmake 中成批生成,分为两类:
- 可双向覆盖的选项(同时生成
CATCH_CONFIG_<X>与CATCH_CONFIG_NO_<X>),包括ANDROID_LOGWRITE、BAZEL_SUPPORT、COLOUR_WIN32、COUNTER、CPP11_TO_STRING、CPP17_BYTE、CPP17_OPTIONAL、CPP17_STRING_VIEW、CPP17_UNCAUGHT_EXCEPTIONS、CPP17_VARIANT、GLOBAL_NEXTAFTER、POSIX_SIGNALS、USE_ASYNC、WCHAR、WINDOWS_SEH、GETENV、EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT、USE_BUILTIN_CONSTANT_P、DEPRECATION_ANNOTATIONS、THREAD_SAFE_ASSERTIONS等; - 单向选项,包括
DISABLE_EXCEPTIONS、DISABLE_EXCEPTIONS_CUSTOM_HANDLER、DISABLE、DISABLE_STRINGIFICATION、ENABLE_ALL_STRINGMAKERS、ENABLE_OPTIONAL_STRINGMAKER、ENABLE_PAIR_STRINGMAKER、ENABLE_TUPLE_STRINGMAKER、ENABLE_VARIANT_STRINGMAKER、EXPERIMENTAL_REDIRECT、FAST_COMPILE、NOSTDOUT、PREFIX_ALL、PREFIX_MESSAGES、WINDOWS_CRTDBG等。
关键语义:把选项设为OFF并不会“关闭”它。要强制禁用某个特性,需要把对应的_NO_形式设为ON。以颜色支持为例,官方给出的行为真值表如下:
-DCATCH_CONFIG_COLOUR_WIN32 | -DCATCH_CONFIG_NO_COLOUR_WIN32 | 结果 |
|---|---|---|
ON | ON | error(配置错误) |
ON | OFF | force-on(强制启用) |
OFF | ON | force-off(强制禁用) |
OFF | OFF | auto-detect(自动检测) |
类似的配置如CATCH_CONFIG_CONSOLE_WIDTH(默认"80")与CATCH_CONFIG_DEFAULT_REPORTER(默认"console")也可作为 CMake cache 变量在 CMake/CatchConfigOptions.cmake 中看到。这些选项最终会在配置阶段写入由 src/catch2/catch_user_config.hpp.in 生成的catch_user_config.hpp,随库一起编译。
三种安装方式
从 Git 仓库安装
如果包管理器提供的 Catch2 版本过旧,可以直接从仓库安装。拥有足够权限时:
$ git clone https://gitcode.com/GitHub_Trending/ca/Catch2.git $ cd Catch2 $ cmake -B build -S . -DBUILD_TESTING=OFF $ sudo cmake --build build/ --target install如果没有超级用户权限,配置时还需指定CMAKE_INSTALL_PREFIX,并让后续find_package(Catch2 ...)的查找路径与之对应。
通过 vcpkg 安装
也可以使用 vcpkg 依赖管理器构建安装 Catch2:
git clone <vcpkg 官方仓库地址> cd vcpkg ./bootstrap-vcpkg.sh ./vcpkg integrate install ./vcpkg install catch2vcpkg 中的 catch2 port 由微软团队成员与社区贡献者维护;若版本过期,可在 vcpkg 仓库上提交 issue 或 pull request 更新。
通过 Bazel 使用
Catch2 是 Bazel Central Registry 的受支持模块(本仓库 MODULE.bazel 即声明module(name = "catch2"),并依赖bazel_skylib、rules_cc、rules_license)。在MODULE.bazel中加入对最新支持版本catch2模块的依赖后,即可在 C++ 测试规则中链接catch2_main:
cc_test( name = "example_test", srcs = ["example_test.cpp"], deps = [ ":example", "@catch2//:catch2_main", ], )结语
围绕 docs/cmake-integration.md 这份文档,本文覆盖了 Catch2 与 CMake 集成的完整链路:从Catch2::Catch2/Catch2::Catch2WithMain两种链接方式(含find_package、add_subdirectory、FetchContent三种接入形态),到以运行期 JSON 枚举为基础的catch_discover_tests自动注册(含全部 13+ 个参数与POST_BUILD/PRE_TEST两种发现模式),再到静态解析的弃用方案ParseAndAddCatchTests、随机分片的catch_add_sharded_tests,以及工程选项、CATCH_CONFIG_*开关真值表和 Git/vcpkg/Bazel 三种安装路径。所有参数与默认值均可在 extras 脚本、CMake 目录与 CMakeLists.txt 源码中找到对应实现,可作为项目接入与排障的一手依据。
【免费下载链接】Catch2A modern, C++-native, test framework for unit-tests, TDD and BDD - using C++14, C++17 and later (C++11 support is in v2.x branch, and C++03 on the Catch1.x branch)项目地址: https://gitcode.com/GitHub_Trending/ca/Catch2
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考