如何解决使用FFF与Google Test在C/C++中Mock函数后无法调用其真实实现的问题?
解决GTest+FFF中Mock与真实函数冲突的问题
这是个很典型的Mock隔离问题,我用过GTest+FFF处理过类似场景,给你几个可行的解决方案:
方案1:按测试目标拆分独立测试文件(推荐)
核心思路是把测试A1和测试B1的代码放到不同的源文件中,让它们各自编译链接,完全隔离符号环境:
测试A1的文件(比如
test_a1.cpp):
在这里定义B1/B2/B3的Mock,独立编译后生成的测试二进制会优先链接Mock版本的B函数,不会影响其他测试:#include "a1.h" #include "gtest/gtest.h" #include "fff.h" // 用FFF宏Mock B1/B2/B3 FAKE_VALUE_FUNC(int, B1, int); FAKE_VALUE_FUNC(void, B2); FAKE_VALUE_FUNC(bool, B3, const char*); TEST(A1Tests, HandlesNormalCase) { // 设置Mock B1/B2/B3的预期行为 B1_return_val = 42; // 调用A1并验证结果 auto result = A1(); EXPECT_EQ(result, 42); // 重置Fake状态,避免影响后续测试 RESET_FAKE(B1); RESET_FAKE(B2); RESET_FAKE(B3); }测试B1的文件(比如
test_b1.cpp):
这里不定义B1的Mock,直接链接真实的B1实现,同时Mock它依赖的C1/C2/C3:#include "b1.h" #include "gtest/gtest.h" #include "fff.h" // 给C1/C2/C3添加__attribute__((weak))后,用FFF Mock它们 FAKE_VALUE_FUNC(int, C1, float); FAKE_VALUE_FUNC(void, C2); TEST(B1Tests, CalculatesCorrectly) { // 设置C1/C2的Mock行为 C1_return_val = 100; // 直接调用真实的B1函数 auto result = B1(5); EXPECT_EQ(result, 500); RESET_FAKE(C1); RESET_FAKE(C2); }
优点:完全隔离不同测试的符号环境,不会出现互相干扰的情况,代码结构清晰,容易维护。
缺点:需要多创建几个测试文件,但这是大型项目中单元测试的规范做法。
方案2:动态切换Mock与真实函数指针
如果不想拆分文件,可以利用weak符号的特性和FFF的函数指针替换能力,在测试B1前临时恢复真实的B1实现:
首先在真实的
b1.cpp中暴露真实函数的指针:// b1.h extern int (*real_B1)(int); // b1.cpp #include "b1.h" __attribute__((weak)) int B1(int arg) { // 真实实现代码 return arg * 10; } // 保存真实B1的函数指针 int (*real_B1)(int) = B1;在测试文件中,切换Mock和真实实现:
#include "a1.h" #include "b1.h" #include "gtest/gtest.h" #include "fff.h" // 定义B1的Mock FAKE_VALUE_FUNC(int, B1, int); // 定义C1/C2的Mock FAKE_VALUE_FUNC(int, C1, float); TEST(A1Tests, UsesMockB1) { B1_return_val = 42; EXPECT_EQ(A1(), 42); RESET_FAKE(B1); } TEST(B1Tests, UsesRealB1) { // 保存当前Mock的B1指针 auto original_B1_ptr = B1; // 切换到真实的B1实现 B1 = real_B1; // 设置C1的Mock行为 C1_return_val = 20; // 调用真实B1 EXPECT_EQ(B1(5), 100); // 测试完成后恢复Mock的B1,避免影响其他测试 B1 = original_B1_ptr; RESET_FAKE(C1); }
优点:不需要拆分文件,在同一个测试套件中就能切换实现。
缺点:需要修改真实代码暴露函数指针,增加了耦合度,且如果有多个Mock函数需要切换,代码会变得繁琐。
方案3:用编译宏控制Mock的启用
通过宏定义来决定是否编译Mock的B函数,在不同测试中通过编译选项传递宏:
在测试文件中添加宏判断:
#include "gtest/gtest.h" #include "fff.h" #ifdef MOCK_B_FUNCTIONS // 仅在测试A1时编译B1/B2/B3的Mock FAKE_VALUE_FUNC(int, B1, int); FAKE_VALUE_FUNC(void, B2); #endif #ifdef MOCK_C_FUNCTIONS // 仅在测试B1时编译C1/C2的Mock FAKE_VALUE_FUNC(int, C1, float); #endif #ifdef MOCK_B_FUNCTIONS TEST(A1Tests, ...) { // 测试A1的逻辑 } #endif #ifdef MOCK_C_FUNCTIONS TEST(B1Tests, ...) { // 测试B1的逻辑 } #endif在编译时给不同的测试目标传递不同的宏:
比如用CMake的话:# 测试A1的目标 add_executable(test_a1 test.cpp) target_compile_definitions(test_a1 PRIVATE MOCK_B_FUNCTIONS) target_link_libraries(test_a1 gtest_main fff your_lib) # 测试B1的目标 add_executable(test_b1 test.cpp) target_compile_definitions(test_b1 PRIVATE MOCK_C_FUNCTIONS) target_link_libraries(test_b1 gtest_main fff your_lib)
优点:可以在同一个源文件中管理不同测试,通过编译宏控制符号的存在。
缺点:宏的管理会增加复杂度,尤其是测试用例较多时,容易出错。
内容的提问来源于stack exchange,提问作者Eyal Gerber
相关产品推荐
相关产品推荐

