news 2026/9/13 19:51:02

Catch2 CMake 集成完全指南:从链接 Target、自动注册测试到分片与安装

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Catch2 CMake 集成完全指南:从链接 Target、自动注册测试到分片与安装

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::Catch2Catch2::Catch2WithMain

Catch2 的 CMake 构建会导出两个 target:Catch2::Catch2Catch2::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_SOURCESINTERFACE_SOURCESREPORTER_SOURCESMATCHER_SOURCESGENERATOR_SOURCESBENCHMARK_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.pccatch2-with-main.pc(模板见 CMake/catch2.pc.in 与 CMake/catch2-with-main.pc.in),为使用 pkg-config 的项目提供同样的链接信息。

自动测试注册:把TEST_CASE变成 CTest 用例

Catch2 仓库的extras目录提供了三套辅助脚本:

  1. Catch.cmake(及其依赖CatchAddTests.cmake)——推荐方案;
  2. ParseAndAddCatchTests.cmake(已弃用);
  3. 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_testset_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_PATHSDL_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/PATHDYLD_FRAMEWORK_PATH),在发现测试和真正执行测试时都会生效。

仓库自带的使用实例位于 tests/TestScripts/DiscoverTests/CMakeLists.txt:它以add_subdirectory引入 Catch2,链接Catch2::Catch2WithMain,并同时使用了ADD_TAGS_AS_LABELSDISCOVERY_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_VERBOSEON时打印调试信息OFF
PARSE_CATCH_TESTS_NO_HIDDEN_TESTSON时不注册隐藏测试(带[.][.foo]标签)OFF
PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAMEON时把 fixture 类名加入 CTest 测试名ON
PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAMEON时把 target 名加入 CTest 测试名ON
PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDSON时把测试文件加入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_TESTINGON且不是作为子工程使用时,构建 Catch2 测试二进制ON
CATCH_INSTALL_DOCSON时把文档安装到系统ON
CATCH_INSTALL_EXTRASON时把extras(上述 CMake 脚本、调试器辅助文件)一并安装ON
CATCH_DEVELOPMENT_BUILDON时按“开发 Catch2 本身”配置构建(启用测试工程、警告等)OFF
CATCH_ENABLE_REPRODUCIBLE_BUILDON时为构建添加可复现性编译参数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_SURROGATESON时逐个独立编译 Catch2 的每个头文件(生成“代理翻译单元”),以验证它们自给自足,默认OFF

从源码结构看,顶层 CMakeLists.txt 还通过cmake_dependent_option声明了CATCH_BUILD_BENCHMARKS(构建 benchmarks)、CATCH_ENABLE_COVERAGE(生成覆盖率)、CATCH_ENABLE_CONFIGURE_TESTSCATCH_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_LOGWRITEBAZEL_SUPPORTCOLOUR_WIN32COUNTERCPP11_TO_STRINGCPP17_BYTECPP17_OPTIONALCPP17_STRING_VIEWCPP17_UNCAUGHT_EXCEPTIONSCPP17_VARIANTGLOBAL_NEXTAFTERPOSIX_SIGNALSUSE_ASYNCWCHARWINDOWS_SEHGETENVEXPERIMENTAL_STATIC_ANALYSIS_SUPPORTUSE_BUILTIN_CONSTANT_PDEPRECATION_ANNOTATIONSTHREAD_SAFE_ASSERTIONS等;
  • 单向选项,包括DISABLE_EXCEPTIONSDISABLE_EXCEPTIONS_CUSTOM_HANDLERDISABLEDISABLE_STRINGIFICATIONENABLE_ALL_STRINGMAKERSENABLE_OPTIONAL_STRINGMAKERENABLE_PAIR_STRINGMAKERENABLE_TUPLE_STRINGMAKERENABLE_VARIANT_STRINGMAKEREXPERIMENTAL_REDIRECTFAST_COMPILENOSTDOUTPREFIX_ALLPREFIX_MESSAGESWINDOWS_CRTDBG等。

关键语义:把选项设为OFF并不会“关闭”它。要强制禁用某个特性,需要把对应的_NO_形式设为ON。以颜色支持为例,官方给出的行为真值表如下:

-DCATCH_CONFIG_COLOUR_WIN32-DCATCH_CONFIG_NO_COLOUR_WIN32结果
ONONerror(配置错误)
ONOFFforce-on(强制启用)
OFFONforce-off(强制禁用)
OFFOFFauto-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 catch2

vcpkg 中的 catch2 port 由微软团队成员与社区贡献者维护;若版本过期,可在 vcpkg 仓库上提交 issue 或 pull request 更新。

通过 Bazel 使用

Catch2 是 Bazel Central Registry 的受支持模块(本仓库 MODULE.bazel 即声明module(name = "catch2"),并依赖bazel_skylibrules_ccrules_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_packageadd_subdirectoryFetchContent三种接入形态),到以运行期 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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 19:50:22

Archon Quick Start:5 分钟跑通你的第一个 AI 编码工作流

Archon Quick Start&#xff1a;5 分钟跑通你的第一个 AI 编码工作流 【免费下载链接】Archon The first open-source harness builder for AI coding. Make AI coding deterministic and repeatable. 项目地址: https://gitcode.com/GitHub_Trending/archon3/Archon 本…

作者头像 李华
网站建设 2026/9/13 19:49:46

Linux内核设备驱动开发核心原理与实战

1. 项目概述&#xff1a;这不是写个hello world就能交差的底层工程 “Linux设备驱动开发”这七个字&#xff0c;听起来像教科书目录里一个不起眼的章节&#xff0c;但在我带过的二十多届嵌入式团队新人里&#xff0c;超过七成的人在真正动手写第一个字符设备驱动前&#xff0c;…

作者头像 李华
网站建设 2026/9/13 19:48:29

方框不再来:用PDF补丁丁完成PDF字体嵌入

方框不再来&#xff1a;用PDF补丁丁完成PDF字体嵌入 【免费下载链接】PDFPatcher PDF补丁丁——PDF工具箱&#xff0c;可以编辑书签、剪裁旋转页面、解除限制、提取或合并文档&#xff0c;探查文档结构&#xff0c;提取图片、转成图片等等 项目地址: https://gitcode.com/Git…

作者头像 李华
网站建设 2026/9/13 19:48:26

工业级多协议远程控制中枢JY-DAM0808B深度解析

1. 这不是“智能插座”&#xff0c;是工业级远程控制中枢的真实面目JY-DAM0808B——光看型号&#xff0c;很多人第一反应是“又一个带WiFi的继电器板”。但如果你真把它当普通IoT模块用&#xff0c;三分钟内就会在产线上栽跟头。我去年在东莞一家汽车零部件厂做设备联网改造时&…

作者头像 李华