如何让Google Test保留静态局部变量值,解决CMake make test重置问题
问题根因
你遇到的现象本质是CMake配套的CTest工具的默认行为导致的:
如果你使用了
gtest_discover_tests命令注册GTest测试用例,CMake会在构建/测试阶段自动扫描二进制内的所有TEST/TEST_F定义,把每一个GTest用例都注册为独立的CTest测试项。执行make test时,CTest会为每一个测试项单独启动测试二进制进程,通过--gtest_filter参数指定只运行当前用例,因此函数内的静态局部变量每次都会被初始化,才会出现和直接运行整个二进制结果不一致的问题。
解决方案
方案1:调整CMake测试注册逻辑,不拆分单个测试用例(最贴合你需求)
放弃自动拆分GTest用例的逻辑,直接把整个测试二进制作为单个CTest测试项,运行时只会启动一次进程,所有测试用例按GTest默认顺序在同一个进程内执行:
- 删除你CMakeLists.txt中原有的
gtest_discover_tests(你的测试目标名)行 - 改用
add_test直接注册整个测试二进制:
add_test( NAME 你的测试项名称(比如MyModuleUnitTests) COMMAND 你的测试目标名 )
这个方案的优点是不需要修改业务代码和测试代码,完全符合你要的「所有测试在同一个实例按顺序运行」的要求,缺点是CTest只会显示整个测试包的最终结果,不会单独统计每个GTest用例的运行状态,你需要看测试输出才能知道具体哪个用例失败。
方案2:保留拆分测试用例的能力,修改业务代码添加测试专用重置接口(单元测试最佳实践)
单元测试的设计原则是每个用例完全独立,不依赖其他用例的运行状态和执行顺序,因此更推荐你修改少量业务代码适配测试场景,不需要修改CMake配置:
- 把原函数内的静态局部变量
last_buffer_idx移到文件作用域(仍用static修饰,不对外暴露,不影响原有作用域规则) - 新增单元测试专用的状态重置接口,仅在单元测试编译时生效:
// 原函数所在的.c文件 static uint32_t last_buffer_idx = 0u; #ifdef UNIT_TEST // 仅单元测试编译时导出该接口 void test_reset_myfunction_state(void) { last_buffer_idx = 0u; } #endif process_result_t myFunction(void) { // 原有逻辑,直接使用文件作用域的last_buffer_idx即可,不需要修改原有代码逻辑 ... }
- 在你的GTest测试用例的
SetUp方法中,每个用例运行前调用test_reset_myfunction_state()重置状态,保证每个用例的初始状态一致。
这个方案的优点是完全符合单元测试规范,不管CTest怎么运行测试都不会出现状态不一致的问题,同时保留了CTest拆分单个用例统计结果的能力。
方案3:开启gtest_discover_tests的批量运行模式(需要CMake 3.18+)
如果你既要保留CTest拆分每个用例统计结果的能力,又要让所有用例在同一个进程中运行,可以给gtest_discover_tests添加DISCOVERY_MODE PRE_TEST和XML_OUTPUT_DIR参数:
gtest_discover_tests(你的测试目标名 DISCOVERY_MODE PRE_TEST XML_OUTPUT_DIR ${CMAKE_BINARY_DIR}/test-reports )
这个模式下CTest会一次性启动测试二进制运行所有用例,生成XML测试报告后再解析每个用例的结果分别展示,不会重复启动进程,也能保留单个用例的统计能力。
内容的提问来源于stack exchange,提问作者Walkingbeard

