CMake中单元测试分组构建与目标执行控制问题咨询
CMake单元测试配置问题解答
问题背景
我在CMake中配置了多个测试构建目标,最后一个目标UTestAll用于运行所有测试。请问:
- UTestAll是否会单独构建每个单元测试?若是,是否会导致同一测试被多次构建?有没有办法对单元测试进行分组,使其仅构建一次?
- 是否可以设置选项排除特定目标的执行?例如在Jenkins流水线中不运行UTestAll,但在本地运行该目标。
现有CMake代码片段
Test1
set(THIS UTest1) string(TOUPPER DO_BUILD_${THIS} THIS_FLAG) if (${THIS_FLAG}) add_executable( ${THIS} src/Test1.cpp ) target_include_directories(${THIS} PRIVATE include) target_link_libraries(${THIS} PUBLIC lib1) endif()
Test2
set(THIS UTest2) string(TOUPPER DO_BUILD_${THIS} THIS_FLAG) if (${THIS_FLAG}) add_executable( ${THIS} src/Test2.cpp ) target_include_directories(${THIS} PRIVATE include) target_link_libraries(${THIS} PUBLIC lib2) endif()
Test3
set(THIS UTest3) string(TOUPPER DO_BUILD_${THIS} THIS_FLAG) if (${THIS_FLAG}) add_executable( ${THIS} src/Test3.cpp ) target_include_directories(${THIS} PRIVATE include) target_link_libraries(${THIS} PUBLIC lib3) endif()
Run ALL Tests
set(THIS UTestAll) string(TOUPPER DO_BUILD_${THIS} THIS_FLAG) if (${THIS_FLAG}) add_executable( ${THIS} src/Test1.cpp src/Test2.cpp src/Test3.cpp ) target_include_directories(${THIS} PRIVATE include) target_link_libraries(${THIS} PUBLIC lib1 lib2 lib3) endif()
问题1解答
当前的UTestAll实现会导致重复构建:
- 若同时开启
DO_BUILD_UTEST1和DO_BUILD_UTESTALL,src/Test1.cpp会被编译两次——一次生成UTest1可执行文件,另一次生成UTestAll。Test2、Test3的源文件同理,会造成构建资源浪费。
解决办法
方案1:封装测试代码为静态库
将每个测试的核心逻辑抽成静态库,单个测试目标和UTestAll都链接这些库,源文件仅编译一次:
# 创建测试静态库 add_library(Test1Lib STATIC src/Test1.cpp) target_include_directories(Test1Lib PRIVATE include) target_link_libraries(Test1Lib PUBLIC lib1) add_library(Test2Lib STATIC src/Test2.cpp) target_include_directories(Test2Lib PRIVATE include) target_link_libraries(Test2Lib PUBLIC lib2) add_library(Test3Lib STATIC src/Test3.cpp) target_include_directories(Test3Lib PRIVATE include) target_link_libraries(Test3Lib PUBLIC lib3) # 单个测试目标(仅包含main函数,调用对应库的测试逻辑) set(THIS UTest1) string(TOUPPER DO_BUILD_${THIS} THIS_FLAG) if (${THIS_FLAG}) add_executable(${THIS} src/UTest1Main.cpp) target_link_libraries(${THIS} PUBLIC Test1Lib) endif() # UTestAll目标(统一main函数,调用所有测试库逻辑) set(THIS UTestAll) string(TOUPPER DO_BUILD_${THIS} THIS_FLAG) if (${THIS_FLAG}) add_executable(${THIS} src/UTestAllMain.cpp) target_link_libraries(${THIS} PUBLIC Test1Lib Test2Lib Test3Lib) endif()
方案2:用CTest管理测试(更推荐)
无需手动编写UTestAll,利用CMake自带的CTest框架管理测试用例,通过ctest命令一键运行所有测试:
# 保留原有单个测试目标定义,添加CTest测试用例 if (DO_BUILD_UTEST1) add_test(NAME UTest1 COMMAND UTest1) endif() if (DO_BUILD_UTEST2) add_test(NAME UTest2 COMMAND UTest2) endif() if (DO_BUILD_UTEST3) add_test(NAME UTest3 COMMAND UTest3) endif()
本地执行ctest即可运行所有启用的测试,每个源文件仅编译一次;还能通过ctest -R UTest1单独运行指定测试,灵活性远高于自定义UTestAll。
问题2解答
完全可以通过CMake选项控制目标的构建与执行:
1. 控制UTestAll的构建开关
你的代码已经通过DO_BUILD_UTESTALL控制UTestAll的构建,可在CMakeLists开头默认开启该选项:
option(DO_BUILD_UTESTALL "Build UTestAll target" ON)
Jenkins流水线构建时,通过CMake参数关闭它:
cmake .. -DDO_BUILD_UTESTALL=OFF
此时UTestAll不会被构建,自然不会执行。
2. 控制执行阶段
若需构建UTestAll但不执行,或仅运行单个测试,可在Jenkins执行步骤指定目标:
- 仅运行单个测试:
make UTest1 && ./UTest1 - 跳过UTestAll:直接执行UTest1、UTest2、UTest3,不调用UTestAll可执行文件。
若使用CTest,还能通过ctest -R UTest[1-3]指定运行特定测试,或直接执行ctest运行所有启用的测试。
内容的提问来源于stack exchange,提问作者xyf
相关产品推荐
相关产品推荐

