C++ GoogleTest测试调用单例的类:Mock方法未执行问题
在GoogleTest中解决单例依赖Mock失效问题
核心问题出在单例初始化时机与Mock对象替换时机不匹配,导致测试时调用的是原始MockedUtil实例而非你Mock的版本。以下是无需依赖注入的解决方案:
1. 给MockedUtil单例添加测试替换接口
对原单例做最小改动,保留原有逻辑的同时支持测试时替换实例:
// MockedUtil.h class MockedUtil { private: static MockedUtil* instance_; MockedUtil() = default; // 私有构造 public: static MockedUtil& GetInstance() { if (!instance_) { instance_ = new MockedUtil(); } return *instance_; } // 测试专用:替换单例实例 static void SetInstanceForTest(MockedUtil* mock_instance) { delete instance_; instance_ = mock_instance; } // 必须声明为虚函数,否则GoogleMock无法覆盖行为 virtual bool readJSONFile(const std::string& path) { // 原始实现 return false; } };
2. 编写Mock类并在测试中替换单例
用GoogleMock创建MockedUtil的子类,在测试用例初始化阶段替换单例:
#include <gmock/gmock.h> #include <gtest/gtest.h> #include "MockedUtil.h" #include "ConcreteSingleton.h" #include "ClassToBeTested.h" class MockMockedUtil : public MockedUtil { public: MOCK_METHOD(bool, readJSONFile, (const std::string& path), (override)); }; TEST(ClassToBeTestedTest, FooReachesCoveredLine) { // 创建Mock实例并替换单例 auto mock_util = new MockMockedUtil(); MockedUtil::SetInstanceForTest(mock_util); // 设置预期行为:调用readJSONFile时返回true EXPECT_CALL(*mock_util, readJSONFile(testing::_)) .WillOnce(testing::Return(true)); // 重置ConcreteSingleton,确保它获取Mock后的MockedUtil实例 ConcreteSingleton::ResetForTest(); // 执行测试逻辑 ClassToBeTested tested_obj; tested_obj.foo(); // 验证预期分支是否执行(比如断言分支对应的状态/输出) // ASSERT_TRUE(tested_obj.GetBranchExecutionFlag()); // 清理:恢复原始单例,避免影响其他测试 ConcreteSingleton::ResetForTest(); MockedUtil::SetInstanceForTest(nullptr); }
3. 给ConcreteSingleton添加重置接口
如果ConcreteSingleton在测试前已初始化并持有原始MockedUtil实例,需添加重置方法让它重新获取Mock后的实例:
// ConcreteSingleton.h class ConcreteSingleton { private: static ConcreteSingleton* instance_; MockedUtil& util_; ConcreteSingleton() : util_(MockedUtil::GetInstance()) {} public: static ConcreteSingleton& GetInstance() { if (!instance_) { instance_ = new ConcreteSingleton(); } return *instance_; } // 测试专用:重置单例 static void ResetForTest() { delete instance_; instance_ = nullptr; } bool readJSON(const std::string& path) { return util_.readJSONFile(path); } };
关键注意事项
- 初始化顺序:必须在
ConcreteSingleton初始化前替换MockedUtil单例,否则后者会持有原始实例。 - 测试隔离:每个测试用例结束后重置单例,避免不同测试的状态污染。
- 虚函数要求:
MockedUtil的readJSONFile必须是虚函数,这是GoogleMock覆盖方法的前提。
内容的提问来源于stack exchange,提问作者roschach
相关产品推荐
相关产品推荐

