如何Mock嵌套结构体返回对象?C++ GMock问题求助
这个问题我之前踩过坑,核心原因是对象切片(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); }
关键注意事项
- 永远不要用值类型返回多态对象:只要涉及到基类和派生类的多态场景,返回值必须是指针或引用,否则必然触发对象切片。
- 虚析构函数必须加:如果基类的析构函数不是虚函数,智能指针销毁对象时只会调用基类的析构函数,导致派生类的资源泄漏。
- 转移智能指针所有权:用
Return(ByMove(...))而不是直接返回智能指针,避免重复释放内存。
内容的提问来源于stack exchange,提问作者alice

