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

如何在Google Test中Mock类的私有成员对象方法?

解决GMock中Mock私有成员对象方法的问题

问题背景

现有代码结构如下:

Foo类相关定义

Ifoo.h

class IFoo {
public:
    virtual ~IFoo() = default;
    virtual void foo_function() const = 0;
};

foo.h

#include "Ifoo.h"

class Foo : public IFoo {
public:
    virtual ~Foo() = default;
    void foo_function() const override {};
};

foo.cpp

#include <iostream>
#include "foo.h"

void Foo::foo_function() const {
    std::cout<<"Foo function is calling some library function\n";
}

Base类定义

base.h

#include "foo.h"

class Base {
public:
    void caller() const {};
private:
    Foo obj{};
};

base.cpp

#include "base.h"
#include <iostream>

void Base::caller() const
{
    std::cout << "Base class is calling foo class: " << std::endl;
    obj.foo_function();
}

现有测试代码

Mock类定义:

#include "Ifoo.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>

class MockFoo : public IFoo
{
    MOCK_METHOD(void, foo_function, (), (const override));
};

测试用例:

#include "mockFoo.h"
#include "base.h"

class BaseMock : public ::testing::Test
{
public:
    Base base_obj{};
};

TEST_F(BaseMock, test1)
{
    MockFoo mocked_foo{};

    EXPECT_CALL(mocked_foo, foo_function()).Times(1);
    base_obj.caller();
}

当前核心问题:Base::caller()始终调用真实的Foo::foo_function(),因为Base内部持有私有Foo实例,无法直接替换为Mock对象。

可行解决方案

方案1:依赖注入重构Base类(推荐)

这是最符合面向对象设计原则的方式,通过构造函数将IFoo实例注入Base,而非在内部直接实例化具体的Foo类,既保留原有生产环境行为,又支持测试时替换Mock对象。

修改后的base.h:

#include "Ifoo.h"
#include <memory>

class Base {
public:
    // 默认构造函数使用真实Foo,保持原有业务逻辑不变
    Base() : obj(std::make_unique<Foo>()) {}
    // 测试专用构造函数,允许注入自定义IFoo实例
    explicit Base(std::unique_ptr<IFoo> foo_obj) : obj(std::move(foo_obj)) {}

    void caller() const;
private:
    std::unique_ptr<IFoo> obj; // 改为接口指针,依赖抽象而非具体实现
};

修改后的base.cpp:

#include "base.h"
#include <iostream>

void Base::caller() const
{
    std::cout << "Base class is calling foo class: " << std::endl;
    obj->foo_function(); // 调用接口方法,适配任意IFoo实现
}

更新后的测试代码:

#include "mockFoo.h"
#include "base.h"
#include <memory>

TEST(BaseTest, test1)
{
    auto mock_foo = std::make_unique<MockFoo>();
    MockFoo* mock_ptr = mock_foo.get();
    Base base_obj(std::move(mock_foo));

    EXPECT_CALL(*mock_ptr, foo_function()).Times(1);
    base_obj.caller();
}

方案2:使用友元(不推荐,破坏封装)

如果无法对Base类进行大规模重构,可以添加测试专属的友元接口,允许测试代码替换私有成员。这种方式会破坏封装性,仅作为临时过渡方案。

修改base.h:

#include "foo.h"
#include "Ifoo.h"

// 提前声明测试类
class BaseTest;

class Base {
public:
    void caller() const;

    // 仅允许测试类访问的成员替换方法
    friend class BaseTest;
    void set_foo(const IFoo* new_foo) {
        obj = new_foo;
    }
private:
    const IFoo* obj;
    Foo default_foo; // 保留默认真实对象,保证生产环境行为
};

测试代码调整:

#include "mockFoo.h"
#include "base.h"

class BaseTest : public ::testing::Test
{
public:
    Base base_obj;
};

TEST_F(BaseTest, test1)
{
    MockFoo mocked_foo;
    base_obj.set_foo(&mocked_foo);

    EXPECT_CALL(mocked_foo, foo_function()).Times(1);
    base_obj.caller();
}

方案3:模板化Base类

将Base类模板化,把依赖的Foo类作为模板参数,测试时传入MockFoo类型。这种方式适合无法使用依赖注入的场景,但会增加代码复杂度。

修改后的base.h:

#include "Ifoo.h"
#include "foo.h"

template<typename FooType = Foo>
class Base {
public:
    void caller() const {
        std::cout << "Base class is calling foo class: " << std::endl;
        obj.foo_function();
    }

    // 测试专用接口,用于获取内部对象
    FooType& get_foo() { return obj; }
    const FooType& get_foo() const { return obj; }
private:
    FooType obj{};
};

测试代码:

#include "mockFoo.h"
#include "base.h"

TEST(BaseTest, test1)
{
    Base<MockFoo> base_obj;
    auto& mock_foo = base_obj.get_foo();

    EXPECT_CALL(mock_foo, foo_function()).Times(1);
    base_obj.caller();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 05:50:20