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

如何为多继承适配器类编写GTest?Mock对象未按预期工作

问题根源与解决方案

问题本质

测试失败的核心原因有两点:

  1. 静态绑定导致无多态行为:BAdapter构造函数中用A::init_A、C::init_C的方式直接调用基类方法,属于静态绑定,不会触发虚函数的多态机制,即使基类有Mock子类,也无法拦截这些调用。
  2. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 18:47:19