如何为多继承适配器类编写GTest?Mock对象未按预期工作
问题根源与解决方案
问题本质
测试失败的核心原因有两点:
- 静态绑定导致无多态行为:
BAdapter构造函数中用A::init_A、C::init_C的方式直接调用基类方法,属于静态绑定,不会触发虚函数的多态机制,即使基类有Mock子类,也无法拦截这些调用。 - Mock实例与被测对象无关:
MockAC是独立的类实例,和BAdapter的基类部分没有任何关联,自然无法捕获BAdapter内部的基类方法调用。
解决方案一:重构为组合模式(推荐)
将BAdapter从继承A、C改为持有A、C的实例,通过构造函数注入依赖,测试时可直接传入MockAC对象,完美拦截方法调用。
修改BAdapter代码
class BAdapter { private: A& m_a; C& m_c; public: // 构造函数注入A和C的实例,替换原有的继承关系 BAdapter(A& a, C& c) : m_a(a), m_c(c) { auto boundACallback = std::bind(&BAdapter::service_state_A, this, std::placeholders::_1); auto boundCCallback = std::bind(&BAdapter::service_state_C, this, std::placeholders::_1); // 通过成员变量调用方法,而非直接调用基类 m_a.init_A(boundACallback); m_c.init_C(boundCCallback); } // 保留原有业务逻辑方法 void service_state_A(ESTATE const& state) { /* 原有实现 */ } void service_state_C(ESTATE const& state) { /* 原有实现 */ } };
修改测试代码
class MockAC : public A, public C { public: MOCK_METHOD1(init_A, void(TServerStateCallback&)); MOCK_METHOD1(init_C, void(TServerStateCallback&)); }; TEST_F(BAdapterTest, ConstructorTest) { auto mockAC = std::make_unique<MockAC>(); // 对Mock实例设置调用期望 EXPECT_CALL(*mockAC, init_A(_)) .WillOnce(Invoke([](TServerStateCallback& callback) { callback(ESTATE::AVAILABLE); })); EXPECT_CALL(*mockAC, init_C(_)) .WillOnce(Invoke([](TServerStateCallback& callback) { callback(ESTATE::AVAILABLE); })); // 注入Mock实例创建BAdapter,此时init调用会被Mock拦截 BAdapter b(*mockAC, *mockAC); Mock::VerifyAndClearExpectations(mockAC.get()); }
解决方案二:修改基类为虚方法(兼容原有继承结构)
如果无法重构BAdapter的继承关系,可将A、C的init方法改为虚函数,让BAdapter的调用触发多态,再通过测试专用的子类或模板类替换基类为MockAC。
第一步:修改基类A、C
class A { public: // 将init_A改为虚函数,添加虚析构确保多态安全 virtual void init_A(TServerStateCallback& callback) { /* 原有实现 */ } virtual ~A() = default; }; class C { public: virtual void init_C(TServerStateCallback& callback) { /* 原有实现 */ } virtual ~C() = default; };
第二步:模板化BAdapter实现基类可替换
// 用模板参数化基类,默认使用原有A、C的组合(这里用MockAC作为统一基类,因为它同时继承A、C) template<typename BaseAC = A> class BAdapterTemplate : public BaseAC { public: // 将初始化逻辑移到单独的init方法,避免构造函数提前调用导致期望无法设置 void init() { auto boundACallback = std::bind(&BAdapterTemplate::service_state_A, this, std::placeholders::_1); auto boundCCallback = std::bind(&BAdapterTemplate::service_state_C, this, std::placeholders::_1); // 直接调用虚方法,不再用基类限定符 this->init_A(boundACallback); this->init_C(boundCCallback); } void service_state_A(ESTATE const& state) { /* 原有实现 */ } void service_state_C(ESTATE const& state) { /* 原有实现 */ } };
第三步:修改测试代码
TEST_F(BAdapterTest, ConstructorTest) { // 使用MockAC作为模板参数创建被测对象 BAdapterTemplate<MockAC> b; // 设置Mock调用期望 EXPECT_CALL(b, init_A(_)) .WillOnce(Invoke([](TServerStateCallback& callback) { callback(ESTATE::AVAILABLE); })); EXPECT_CALL(b, init_C(_)) .WillOnce(Invoke([](TServerStateCallback& callback) { callback(ESTATE::AVAILABLE); })); // 主动调用初始化方法,触发Mock拦截 b.init(); Mock::VerifyAndClearExpectations(&b); }
方案对比
- 组合模式:符合依赖倒置原则,代码解耦,测试灵活性高,是长期维护的最优方案。
- 虚方法+模板:无需大幅修改原有继承结构,但需要修改基类方法,适合临时兼容场景。
内容的提问来源于stack exchange,提问作者harishaj90
相关产品推荐
相关产品推荐

