如何用GTest/GMock测试Parent类调用parent_method时是否触发member_method
解决方案:用模板类实现无多态、无动态分配的测试
想要在不使用多态和动态内存分配的前提下测试Parent的parent_method是否调用了Member的member_method,模板化你的Parent类是最优方案。它依赖编译期多态而非运行时虚函数,所有对象都可以在栈上创建,完美匹配你的需求。
步骤1:重构Parent为模板类
把Member的类型作为模板参数传入Parent,默认使用真实的Member类,这样正常业务代码不需要任何修改。同时我们可以保留栈上的成员变量,完全避免动态分配:
// 真实的Member类(无需修改) class Member { public: void member_method() { // 你的真实业务逻辑 } }; // 重构后的Parent模板类 template<typename MemberType = Member> class Parent { private: MemberType member_; // 栈上成员,无new/delete public: // 如果Member需要构造参数,用完美转发支持任意初始化 template<typename... Args> explicit Parent(Args&&... args) : member_(std::forward<Args>(args)...) {} void parent_method() { member_.member_method(); // 调用成员的方法 } // 为了测试能访问member_,可以提供getter或者把测试类设为友元 MemberType& get_member() { return member_; } const MemberType& get_member() const { return member_; } };
步骤2:编写GMock的Mock类
创建一个模拟Member行为的Mock类,用于测试:
#include <gmock/gmock.h> class MockMember { public: MOCK_METHOD(void, member_method, (), ()); // 模拟member_method };
步骤3:编写GTest测试用例
用MockMember作为模板参数实例化Parent,然后验证调用:
#include <gtest/gtest.h> TEST(ParentTest, ParentMethodCallsMemberMethod) { // 栈上创建Parent和MockMember,无动态分配 Parent<MockMember> parent; MockMember& mock_member = parent.get_member(); // 预期member_method被调用一次 EXPECT_CALL(mock_member, member_method()).Times(1); // 触发测试行为 parent.parent_method(); }
为什么这个方案符合你的要求?
- 无运行时多态:模板是编译期处理的,不需要虚函数、基类指针这些运行时多态的元素
- 无动态内存分配:
Parent和MockMember都在栈上创建,完全避开了new/delete - 业务代码无侵入:默认模板参数是真实的
Member,正常使用时依然可以写Parent parent;,不需要修改原有业务逻辑 - 测试逻辑清晰:直接通过Mock验证方法调用,符合GTest/GMock的最佳实践
补充:如果无法修改Parent类?
如果你因为某些原因不能修改Parent的源代码,还有一个变通方案:使用依赖注入的变体,把Member作为Parent构造函数的参数传入,但用栈上对象而非指针:
// 修改后的Parent(如果允许的话) class Parent { private: Member& member_; // 引用而非指针,避免动态分配 public: // 传入栈上的Member对象引用 explicit Parent(Member& member) : member_(member) {} void parent_method() { member_.member_method(); } }; // 测试时: TEST(ParentTest, ParentMethodCallsMemberMethod) { MockMember mock_member; // 栈上Mock Parent parent(mock_member); // 传入引用 EXPECT_CALL(mock_member, member_method()).Times(1); parent.parent_method(); }
不过这个方案需要修改Parent的构造函数,而模板方案的侵入性更小,更推荐前者。
内容的提问来源于stack exchange,提问作者eXpl0it3R
相关产品推荐
相关产品推荐

