You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决使用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实现:

  1. 首先在真实的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;
    
  2. 在测试文件中,切换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函数,在不同测试中通过编译选项传递宏:

  1. 在测试文件中添加宏判断:

    #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
    
  2. 在编译时给不同的测试目标传递不同的宏:
    比如用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 04:42:39