11、CTest配置与构建测试:ctest_config、ctest_build、ctest_test命令详解

聊到CTest,很多人第一反应就是「跑测试」。其实CTest远不止跑测试这么简单。我个人习惯把CTest看作一个测试流程的调度中心——它帮你配置、构建、然后执行测试。今天我们就来拆解这三个核心命令:ctest_configctest_buildctest_test

说白了,这三个命令对应了测试的三个阶段:

  • 配置阶段:告诉CTest去哪里找测试项目,用什么配置
  • 构建阶段:编译被测代码
  • 执行阶段:运行测试并收集结果

你想想看,如果每次都要手动敲cmake、make、ctest,那多麻烦。CTest提供的这三个命令,就是让你能一键走完整个流程。

11.1 ctest_config:配置测试环境

ctest_config 的作用是读取并应用CTest的配置。它不直接调用CMake,而是告诉CTest「我要用哪个构建配置」。嗯,这里要注意:它和 cmake -C 不是一回事。

我在项目中遇到过这样的情况:同一个项目,Debug和Release的测试用例不一样。Debug版本要跑内存检查,Release版本只跑功能测试。这时候 ctest_config 就派上用场了。

核心用法:

# 指定构建配置为 Debug
ctest_config -C Debug

# 指定配置文件
ctest_config -C MyConfig.cmake

这个命令通常用在CDash仪表盘集成时。它会读取 CTestConfig.cmake 文件,里面定义了项目名称、版本、提交信息等元数据。

小技巧: 如果你在本地调试,其实很少单独用 ctest_config。它更多是给持续集成系统用的。我个人习惯在 CTestConfig.cmake 里只放最必要的配置,别塞太多东西。

11.2 ctest_build:一键构建测试项目

ctest_build 是三个命令里最实用的一个。它会自动调用底层的构建工具(make、ninja、msbuild等),不需要你手动敲构建命令。

为什么会需要这个?因为CTest需要知道构建的输出去哪里找测试目标。如果你手动构建,CTest可能找不到测试二进制文件。

# 构建所有目标
ctest_build

# 只构建测试相关目标
ctest_build --target test_target

# 并行构建,用4个核
ctest_build -j4

我曾经踩过一个坑:在Windows上用Visual Studio构建,忘记指定配置类型。结果CTest去Debug目录找测试,我构建的是Release版本。嗯,从那以后我养成了习惯——构建时一定带上 -C 参数。

避坑指南: 我曾经在CI脚本里直接用 ctest_build 不加任何参数,结果构建了全部目标,包括文档、示例、测试工具……整个构建花了40分钟。后来我改成只构建测试目标,时间缩短到5分钟。记住:--target 参数是你的好朋友。

11.3 ctest_test:执行测试并收集结果

这是CTest的核心命令。它会查找所有通过 add_test() 注册的测试,然后逐个执行。

# 运行所有测试
ctest_test

# 按标签筛选
ctest_test -L unit

# 排除某些测试
ctest_test -E integration

# 并行执行测试
ctest_test -j4

# 设置超时时间(秒)
ctest_test --timeout 60

我个人最常用的组合是:

ctest_test -j4 --output-on-failure -C Debug

这个命令的意思是:用4个核并行跑测试,如果测试失败就打印详细输出,使用Debug配置。你想想看,如果没有 --output-on-failure,测试失败了你还得重新跑一遍才能看到错误信息,多浪费时间。

11.4 三个命令的协作流程

这三个命令通常不会单独使用。在CDash持续集成场景下,它们的协作流程是这样的:

CTest 三命令协作流程 ctest_config 读取配置,设置构建类型 ctest_build 编译测试目标 ctest_test 执行测试并收集结果 测试结果输出 XML/JSON 格式,可上传CDash 三个命令可以独立使用,也可以组合在 CTest 脚本中 通过 ctest -S script.cmake 一键执行 常用参数:-C (配置类型) | -j (并行数) | --output-on-failure

在实际的CI脚本中,你可能会这样写:

# CTest 驱动脚本示例
cmake_minimum_required(VERSION 3.20)

# 配置阶段
ctest_config(
  CONFIGURATION "${CTEST_CONFIGURATION_TYPE}"
)

# 构建阶段
ctest_build(
  CONFIGURATION "${CTEST_CONFIGURATION_TYPE}"
  TARGET "test_target"
  NUMBER_OF_PARALLEL_JOBS 4
)

# 测试阶段
ctest_test(
  CONFIGURATION "${CTEST_CONFIGURATION_TYPE}"
  PARALLEL_LEVEL 4
  OUTPUT_ON_FAILURE
  EXCLUDE_LABEL "slow"
)

个人经验: 我建议在本地开发时,直接用 ctest -j4 --output-on-failure 就够了。只有在配置CDash持续集成时,才需要把这三个命令拆开写。别过度设计,简单就是美。

11.5 常见问题与避坑

问题 原因 解决方案
ctest_build 找不到构建目录 没有在构建目录下执行 先 cd 到构建目录,或者用 -B 参数指定
测试全部跳过(Skipped) 配置类型不匹配 检查 -C 参数是否和构建时一致
ctest_test 超时 测试死循环或资源等待 设置 --timeout,或者检查测试代码
并行测试数据竞争 测试共享了全局资源 用 RESOURCE_LOCK 或设置测试串行执行

曾经踩过的坑: 有一次我在CI里用 ctest_build 构建,但忘记指定 CONFIGURATION 参数。结果CTest默认用了Debug配置,而测试脚本里写的是Release。构建成功了,但测试全部报「找不到可执行文件」。排查了半天才发现是配置不匹配。从那以后,我所有CTest脚本里都显式指定配置类型。

11.6 实战建议

如果你刚开始接触CTest,我建议你从最简单的开始:

  1. 先用 ctest_test 跑本地测试
  2. 熟悉了再加 -j 并行和 --output-on-failure
  3. 等需要CI集成时,再研究 ctest_configctest_build

说白了,这三个命令就是CTest的「三驾马车」。配置是方向,构建是动力,测试是检验。把它们用好,你的测试流程就能自动化跑起来。

我个人最满意的配置是:本地开发用 ctest -j4 --output-on-failure -C Debug,CI上用完整的 ctest_config + ctest_build + ctest_test 三件套。这样既保证了开发效率,又确保了CI的可靠性。


公众号:蓝海资料掘金营,微信 deep3321