直接继承接口类写Mock类会编译失败,因为Google Mock要求被模拟类的所有虚函数必须public且析构函数必须virtual;若第三方接口析构函数非virtual或为protected,则需封装适配器类绕过限制。

为什么直接继承接口类写 Mock 类会编译失败
Google Mock 要求被模拟的类必须满足两个硬性条件:所有虚函数都得是 public 的,且 析构函数必须是 virtual 的。如果你拿到的是第三方库头文件,里面接口类的析构函数是 protected 或干脆没声明 virtual,MOCK_METHOD 宏展开后会因无法覆写而报错(典型错误:"cannot override a non-virtual function")。这不是 GMock 用法问题,是 C++ 语言限制。
实操建议:
- 先检查原始接口类定义,重点看
~ClassName()是否带virtual;不带就只能封装一层适配器类(见下一条) - 不要试图用
using声明把 protected 析构函数提升为 public —— C++ 不允许这么做 - 若接口类含模板成员函数,GMock 无法模拟(
MOCK_METHOD不支持模板),必须提取为非模板虚函数再包装
如何安全封装不可修改的外部接口类
当原始类无法加 virtual 析构或含非虚函数时,标准解法是定义一个只含纯虚函数的抽象接口(Interface),再写一个桥接实现类(Adapter)去调用真实对象。这样 GMock 只需模拟 Interface,完全绕过原始类限制。
示例场景:某 SDK 提供 NetworkClient 类,无虚析构、Send() 是非虚函数:
立即学习“C++免费学习笔记(深入)”;
class NetworkClient {
public:
bool Send(const std::string& data); // 非虚,不能 mock
// ~NetworkClient() 不存在 virtual
};
你需要手动补一层:
class INetworkClient {
public:
virtual ~INetworkClient() = default;
virtual bool Send(const std::string& data) = 0;
};
class NetworkClientAdapter : public INetworkClient {
NetworkClient real_client_;
public:
bool Send(const std::string& data) override {
return real_client_.Send(data);
}
};
后续所有业务代码依赖 INetworkClient*,测试时用 MockNetworkClient 替换 —— 这样既不碰原始 SDK 头文件,又满足 GMock 要求。
MOCK_METHOD 宏参数顺序和 const 限定符必须严格匹配
GMock 对函数签名的匹配极其严格:返回类型、参数类型、const 修饰、&/&& 引用符,差一个字符都会导致 EXPECT_CALL 不生效,或编译报 "no matching function for call to ..."。
常见坑点:
- 原函数是
std::string GetData() const,Mock 必须写成MOCK_METHOD(std::string, GetData, (), (const))—— 少了(const)括号里的const,就算函数体里写了const也不认 - 参数是
const std::vector<int>&</int>,Mock 中必须写全const std::vector<int>&</int>,不能简写为std::vector<int>&</int>或const std::vector<int></int> - 返回类型带
noexcept?GMock 不支持模拟noexcept函数,得在接口层去掉(否则编译不过)
EXPECT_CALL 作用域和生命周期管理容易引发段错误
Mock 对象本身只是个普通 C++ 对象,它的预期行为(EXPECT_CALL)只在该对象存活期间有效。如果业务代码把 Mock 指针存进单例、全局缓存、或异步回调队列中,而测试函数结束时 Mock 对象已被析构,后续调用就会访问野指针。
关键判断点:
- 确认所有
EXPECT_CALL设置都在 Mock 对象构造之后、析构之前 —— 最稳妥是放在测试函数开头,Mock 对象定义之后立即写 - 避免在
SetUp()中设置EXPECT_CALL却在TearDown()后还让被测代码调用它(比如异步任务未完成) - 用
ON_CALL(...).WillByDefault(Invoke(...))替代频繁的EXPECT_CALL可降低崩溃风险,但要注意默认行为无法捕获“意外调用”
最隐蔽的问题是跨线程:GMock 默认非线程安全,若被测逻辑在子线程调用 Mock,必须手动加锁或改用 testing::StrictMock + 显式同步,否则断言可能漏报或崩溃。


















