如何在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
相关产品推荐
相关产品推荐

