第9章:CTest入门:CTest是什么?CTest与CMake的关系、启用测试(enable_testing)

9.1 CTest到底是什么?

CTest,说白了就是CMake自带的测试工具。它不是第三方库,也不是什么高大上的框架。它就是CMake家族里专门负责跑测试的那个兄弟。

我个人习惯把CTest理解成「测试调度员」。你写好了测试用例,CTest帮你一个个跑,跑完了告诉你哪些过了、哪些挂了、挂了的原因是什么。就这么简单。

我记得刚接触CTest时,总觉得它跟Google Test、Catch2这些测试框架是一回事。后来才搞明白——CTest不负责写测试,它只负责跑测试。测试用例本身还是用你喜欢的框架写,CTest只是那个「监考老师」。

一句话总结:CTest = 测试运行器 + 测试报告生成器。它不写测试,只管跑。

9.2 CTest与CMake的关系

CTest是CMake的一部分。你装了CMake,CTest就自动装好了。它们的关系就像「锅和锅盖」——分不开。

具体来说,CMake负责构建项目,CTest负责测试项目。流程是这样的:

  1. CMake配置项目,生成构建文件
  2. 你编译项目,生成可执行文件
  3. CTest读取CMake中定义的测试,逐个运行
  4. CTest收集结果,生成报告

嗯,这里要注意:CTest本身不编译代码。你得先编译好,它才能跑。我在项目中遇到过有人以为CTest会自动编译测试——结果折腾了半天,测试文件还是旧的。

小技巧:你可以用 ctest --build-and-test 让CTest先编译再测试,但我个人建议分开做。编译归编译,测试归测试,出了问题好排查。

9.3 启用测试:enable_testing()

要让CTest工作,第一步就是在CMakeLists.txt里调用 enable_testing()。这个命令告诉CMake:「嘿,这个项目要跑测试,给我准备好测试环境。」

它的用法极其简单:

# 在顶层CMakeLists.txt中
cmake_minimum_required(VERSION 3.10)
project(MyProject VERSION 1.0)

enable_testing()  # 启用测试支持

# ... 其他构建逻辑 ...

add_executable(my_test test_main.cpp)
add_test(NAME MyBasicTest COMMAND my_test)

你想想看,就这么一行代码,CTest就能接管你的测试流程了。

我曾经踩过的坑:enable_testing() 必须在顶层CMakeLists.txt中调用。如果你在子目录里调用,CTest会报错说找不到测试。别问我怎么知道的——我当年在一个大项目里折腾了整整一下午。

9.4 add_test():注册测试用例

启用测试后,下一步就是用 add_test() 注册具体的测试用例。基本语法:

add_test(NAME <测试名称> COMMAND <可执行文件> [参数...])

举个例子:

add_executable(test_math test_math.cpp)
add_test(NAME MathTest COMMAND test_math)

add_executable(test_string test_string.cpp)
add_test(NAME StringTest COMMAND test_string --verbose)

这里我用了两个不同的测试可执行文件。CTest会依次运行它们,并报告结果。

我个人习惯给测试命名时加上前缀或分类,比如 Unit_MathTestIntegration_DatabaseTest。这样在CTest里可以按模式筛选运行:

# 只跑单元测试
ctest -R "^Unit_"

# 跳过集成测试
ctest -E "^Integration_"

9.5 知识体系总览

下面这张图帮你理清CTest、CMake和测试框架之间的关系:

CTest与CMake集成测试体系 CMake 构建系统 CTest 测试运行器 测试框架 Google Test / Catch2 等 enable_testing() add_test() 编写测试用例 test_xxx.cpp 注册到CMake add_test() 运行测试 ctest 命令 测试报告:通过/失败/超时 CTest 自动收集并汇总

9.6 一个完整的例子

光说不练假把式。我们来看一个完整的项目结构:

my_project/
├── CMakeLists.txt          # 顶层
├── src/
│   ├── CMakeLists.txt
│   ├── main.cpp
│   └── math_utils.cpp
└── tests/
    ├── CMakeLists.txt
    └── test_math_utils.cpp

顶层 CMakeLists.txt:

cmake_minimum_required(VERSION 3.10)
project(MyProject VERSION 1.0)

enable_testing()  # 启用测试

add_subdirectory(src)
add_subdirectory(tests)

tests/CMakeLists.txt:

add_executable(test_math_utils test_math_utils.cpp)
target_link_libraries(test_math_utils PRIVATE MyProjectLib)

add_test(NAME MathUtilsTest COMMAND test_math_utils)

然后编译、测试:

mkdir build && cd build
cmake ..
make
ctest  # 或者 ctest -V 显示详细信息

输出大概长这样:

Test project /path/to/build
    Start 1: MathUtilsTest
1/1 Test #1: MathUtilsTest ...............   Passed    0.02 sec

100% tests passed, 0 tests failed out of 1

个人经验:我习惯在项目根目录放一个 run_tests.sh 脚本,里面就两行:cd build && ctest --output-on-failure。这样团队成员跑测试时不用记命令,直接 ./run_tests.sh 就行。

9.7 常见问题与避坑

问题 原因 解决方法
ctest 报 "No tests were found" 忘记调用 enable_testing() 在顶层 CMakeLists.txt 中添加 enable_testing()
测试一直显示 "Not Run" 测试可执行文件未编译 先运行 make 编译所有目标
测试路径不对 CTest 工作目录问题 使用 WORKING_DIRECTORY 参数指定

我曾经在一个项目里遇到测试一直报「段错误」,排查了半天发现是测试代码里有个野指针。CTest 不会帮你检查代码质量,它只管跑。所以写测试用例时,该做的检查一个都不能少。

好了,CTest 入门就讲到这里。记住三个核心点:enable_testing() 开启测试、add_test() 注册用例、ctest 命令运行测试。把这三点吃透,CTest 的基本用法你就掌握了。


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