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

如何Mock嵌套结构体返回对象?C++ GMock问题求助

解决Google Mock中嵌套结构体Mock失效的问题

这个问题我之前踩过坑,核心原因是对象切片(Object Slicing)——你的Mock对象被“切”成了基类对象,自然不会触发Mock方法的调用。咱们一步步拆解问题和解决方案:

问题根源:值返回导致的对象切片

你的User::getUserBuilder方法返回的是UserBuilder值类型,而不是指针或引用。当你在Mock中返回UserBuilderMock对象时,编译器会自动调用基类的拷贝构造函数,只保留基类UserBuilder的成员和方法,派生类UserBuilderMock中GMock的内部状态(比如期望调用的记录、Mock方法的实现)全部被丢弃了。最终useCase里调用的是真实UserBuilder的方法,根本没走到Mock逻辑,所以才会报fun1 never called。

解决方案:改用指针/智能指针保留多态性

要解决这个问题,必须修改基类的方法签名,让getUserBuilder返回指针或智能指针,这样才能保留多态性,让Mock对象的方法被正确调用。

步骤1:修改基类User和UserBuilder的定义

把getUserBuilder的返回类型改成std::unique_ptr<UserBuilder>,同时确保UserBuilder的析构函数是虚函数(智能指针需要正确销毁派生类对象):

class User { 
public: 
    virtual int loadData(const std::string& filename); 
    // 改为返回智能指针,避免内存泄漏+保留多态
    virtual std::unique_ptr<UserBuilder> getUserBuilder(const std::string& functionName) const; 

    struct UserBuilder { 
        UserBuilder(std::string functionName) : m_functionName{std::move(functionName)} {}; 
        virtual ~UserBuilder() = default; // 虚析构必须加
        virtual UserBuilder& fun1(); 
        virtual UserBuilder& fun2(int32_t num); 
        virtual bool callFunction(); 
    private: 
        std::string m_functionName{}; 
    }; 
};

步骤2:更新UserMock的Mock方法

对应修改getUserBuilder的Mock方法返回类型:

class UserMock : public User { 
public: 
    MOCK_METHOD1(loadData, int(const std::string& filename)); 
    // 匹配基类的返回类型
    MOCK_CONST_METHOD1(getUserBuilder, std::unique_ptr<User::UserBuilder>(const std::string& functionName)); 
};

步骤3:UserBuilderMock保持原样(小细节优化)

只需要调整构造函数的参数传递(用std::move避免拷贝):

struct UserBuilderMock : public User::UserBuilder { 
public: 
    UserBuilderMock(std::string functionName) : User::UserBuilder(std::move(functionName)) {} 
    MOCK_METHOD0(fun1, UserBuilder&()); 
    MOCK_METHOD1(fun2, UserBuilder&(int32_t num)); 
    MOCK_METHOD0(callFunction, bool()); 
};

步骤4:修正useCase函数的逻辑

适配指针返回的情况,同时修正原代码里的笔误(比如loadFile应该是loadData,getUserlBuilder应该是getUserBuilder,原函数返回值错误):

bool useCase(std::unique_ptr<User> userP) { 
    int status = userP->loadData("init"); 
    if (status == 0) { 
        auto builder = userP->getUserBuilder("init"); 
        // 现在builder是智能指针,用->调用方法
        bool result = builder->fun1().fun2(1).callFunction(); 
        return result; 
    } else { 
        return false; 
    } 
}

步骤5:调整测试代码,正确返回Mock对象

现在需要让getUserBuilder返回Mock对象的智能指针,用Return(ByMove(...))转移所有权,同时确保能捕获到Mock对象的调用:

TEST(UseCaseTest, ShouldCallBuilderMethods) {
    auto userMock = std::make_unique<UserMock>(); 
    // 设置loadData返回0,进入builder分支
    ON_CALL(*userMock, loadData("init")).WillByDefault(Return(0)); 

    // 创建Mock builder
    auto builderMock = std::make_unique<UserBuilderMock>("init"); 
    // 保存裸指针,后续设置EXPECT_CALL(因为智能指针会被转移)
    UserBuilderMock* rawBuilder = builderMock.get();

    // 让getUserBuilder转移Mock builder的所有权
    EXPECT_CALL(*userMock, getUserBuilder("init"))
        .WillOnce(Return(ByMove(std::move(builderMock)))); 

    // 设置Mock方法的调用期望
    EXPECT_CALL(*rawBuilder, fun1()).Times(1);
    EXPECT_CALL(*rawBuilder, fun2(1)).Times(1);
    EXPECT_CALL(*rawBuilder, callFunction()).Times(1).WillOnce(Return(true));

    // 执行测试
    bool result = useCase(std::move(userMock));
    EXPECT_TRUE(result);
}

关键注意事项

  1. 永远不要用值类型返回多态对象:只要涉及到基类和派生类的多态场景,返回值必须是指针或引用,否则必然触发对象切片。
  2. 虚析构函数必须加:如果基类的析构函数不是虚函数,智能指针销毁对象时只会调用基类的析构函数,导致派生类的资源泄漏。
  3. 转移智能指针所有权:用Return(ByMove(...))而不是直接返回智能指针,避免重复释放内存。

内容的提问来源于stack exchange,提问作者alice

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 13:52:48