如何在GTest中修改C++公开类变量值以测试分支逻辑
问题场景
现有代码如下:
ClassToMock.hpp
enum class Flavors {Vanilla, Strawberry}; class ClassToMock { public: Flavors flavorName = Flavors::Vanilla; // 其他代码 }
ClassRunner.cpp
class Runner { void main() { // 其他代码 ClassToMock* classToMock = new ClassToMock(); if (classToMock->flavorName == Flavors::Strawberry) { // 需要测试的分支逻辑 } // 其他代码 } }
测试困境
要编写单元测试覆盖Runner::main()里的if分支,需要将ClassToMock的成员变量flavorName设为Flavors::Strawberry,但直接修改变量无效,尝试继承创建Mock类也没效果:
class MockClassToMock : public ClassToMock { public: Flavors flavorName = Flavors::Vanilla; }
当前测试代码:
TEST_F(...) { Runner* runner = new Runner(); EXPECT_NO_THROW(runner->main()); }
解决方案
方案1:给ClassToMock添加构造/setter方法(推荐)
直接修改ClassToMock,提供设置flavorName的合法入口:
// ClassToMock.hpp enum class Flavors {Vanilla, Strawberry}; class ClassToMock { public: Flavors flavorName = Flavors::Vanilla; // 带参数的构造函数 explicit ClassToMock(Flavors flavor) : flavorName(flavor) {} // setter方法 void setFlavor(Flavors flavor) { flavorName = flavor; } // 保留默认构造函数(兼容原有代码) ClassToMock() = default; }
同时改造Runner::main,支持外部注入ClassToMock实例:
// ClassRunner.cpp class Runner { // 让main接受外部传入的实例,无参数时使用默认创建的实例 void main(ClassToMock* mockInstance = nullptr) { ClassToMock* classToMock = mockInstance ? mockInstance : new ClassToMock(); if (classToMock->flavorName == Flavors::Strawberry) { // 分支逻辑 } // 仅释放内部创建的实例 if (!mockInstance) delete classToMock; } }
测试时直接传入指定flavor的实例:
TEST_F(...) { Runner* runner = new Runner(); ClassToMock* mock = new ClassToMock(Flavors::Strawberry); EXPECT_NO_THROW(runner->main(mock)); delete mock; delete runner; }
方案2:依赖注入+Mock类
用工厂模式让Runner支持替换ClassToMock的创建逻辑,配合Mock类实现变量修改:
- 改造
Runner,引入工厂函数:
// ClassRunner.cpp #include <functional> class Runner { public: using ClassFactory = std::function<ClassToMock*()>; ClassFactory factory; // 默认工厂创建原始ClassToMock实例 Runner(ClassFactory f = [](){ return new ClassToMock(); }) : factory(std::move(f)) {} void main() { ClassToMock* classToMock = factory(); if (classToMock->flavorName == Flavors::Strawberry) { // 分支逻辑 } delete classToMock; } }
- 创建Mock类并在测试中使用:
// 测试代码中的Mock类 class MockClassToMock : public ClassToMock { public: // 可选:添加一个空的Mock方法,让Mock框架(如Google Mock)识别 MOCK_METHOD(void, dummy, (), ()); }; TEST_F(...) { MockClassToMock* mock = new MockClassToMock(); mock->flavorName = Flavors::Strawberry; // 让Runner的工厂返回Mock实例 Runner* runner = new Runner([mock](){ return mock; }); EXPECT_NO_THROW(runner->main()); delete runner; // Mock实例已被Runner::main释放,无需重复删除 }
方案3:友元(不推荐,破坏封装)
在ClassToMock中声明测试类为友元,允许测试代码直接修改成员变量:
// ClassToMock.hpp class TestYourClassName; // 替换为你的测试类名 class ClassToMock { friend class TestYourClassName; public: Flavors flavorName = Flavors::Vanilla; // 其他代码 }
测试时结合依赖注入修改变量:
TEST_F(TestYourClassName, ...) { Runner* runner = new Runner(); ClassToMock* mock = new ClassToMock(); mock->flavorName = Flavors::Strawberry; EXPECT_NO_THROW(runner->main(mock)); delete mock; delete runner; }
方案4:内存修改(极端hack,强烈不推荐)
通过重载new运算符拦截ClassToMock的实例创建,强制修改内存中的变量值,这个方法依赖内存布局,兼容性差且不稳定:
TEST_F(...) { ClassToMock* targetInstance = nullptr; // 全局重载new,仅拦截ClassToMock的实例 void* operator new(size_t size) { void* ptr = malloc(size); if (size == sizeof(ClassToMock)) { targetInstance = static_cast<ClassToMock*>(ptr); } return ptr; } Runner* runner = new Runner(); // 执行main前修改已创建的实例变量 if (targetInstance) { targetInstance->flavorName = Flavors::Strawberry; } EXPECT_NO_THROW(runner->main()); delete runner; // 恢复默认new行为(避免影响其他测试) void* operator new(size_t size) = delete; }
内容的提问来源于stack exchange,提问作者coda
相关产品推荐
相关产品推荐

