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

如何在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类实现变量修改:

  1. 改造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;
        }
}
  1. 创建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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 16:40:38