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

测试依赖含模板方法类的被测类的惯用模式探讨

测试依赖含模板方法的公共API类的惯用方案

你提到的这个场景确实很常见——当依赖的公共API包含模板方法时,传统的动态多态Mock(依赖虚函数)就不好直接用了,因为模板方法没法直接声明为虚函数。下面是几种业界常用的解决方案:

1. 提取模板方法核心逻辑到非模板虚接口

这是最通用的思路:如果能修改公共API,就把模板方法里类型无关的核心逻辑抽离到基类的虚函数中,让原模板方法只负责类型相关的处理并调用这个虚函数。这样就能用传统的Mock方式控制核心逻辑了。

示例代码:

// 改造后的公共API
struct InterfaceBase {
    virtual void do_core_stuff(const std::type_info& type) = 0;
    virtual ~InterfaceBase() = default;
};

struct Interface : public InterfaceBase {
    template<class T>
    void foo() {
        // 模板层只处理类型相关逻辑,核心逻辑交给虚函数
        do_core_stuff(typeid(T));
        // 其他类型专属操作...
    }
};

// Mock类
struct InterfaceMock : public InterfaceBase {
    void do_core_stuff(const std::type_info& type) override {
        // 按类型定制Mock行为,比如记录调用次数、返回预设结果
        if (type == typeid(int)) {
            int_call_count++;
        } else if (type == typeid(std::string)) {
            string_call_count++;
        }
    }

    // 用于验证的计数器
    int int_call_count = 0;
    int string_call_count = 0;
};

// 测试代码
void test_tested_class() {
    InterfaceMock mock;
    Interface api{mock};
    TestedClass tested(api);

    tested.run_operations();
    assert(mock.int_call_count == 2);
    assert(mock.string_call_count == 1);
}

如果无法修改原公共API,可以加一层适配类:自己封装一个带虚接口的适配层,把原API的模板方法调用转换成对虚函数的调用,再Mock这个适配层。

2. 用模板参数注入实现编译期Mock

如果被测类可以改成模板类(或者本身就是模板类),可以把依赖的Interface作为模板参数传入,测试时用自定义的Mock模板类替代原API。

示例代码:

// 原公共API(不可修改)
struct Interface {
    template<class T>
    void foo() {
        // 原业务逻辑
    }
};

// 被测类改成模板类,依赖抽象的API类型
template<typename ApiType>
class TestedClass {
public:
    TestedClass(ApiType& api) : api_(api) {}

    void process_data() {
        api_.foo<int>();
        api_.foo<std::string>();
    }

private:
    ApiType& api_;
};

// 测试用的Mock模板类
struct InterfaceMock {
    template<class T>
    void foo() {
        // 针对不同类型的Mock逻辑,比如记录调用次数
        static int call_count = 0;
        call_count++;
    }

    // 提供验证方法
    template<class T>
    int get_call_count() {
        // 可以用std::map<std::type_index, int>来存储不同类型的调用次数
        static int count = 0;
        return count;
    }
};

// 测试代码
void test_tested_class() {
    InterfaceMock mock;
    TestedClass<InterfaceMock> tested(mock);

    tested.process_data();
    assert(mock.get_call_count<int>() == 1);
}

这种方法不需要修改原API,属于编译期多态的思路,适合被测类本身具备模板扩展性的场景。

3. 基于策略模式适配有限类型

如果被测类只用到模板方法的有限几种类型,可以把模板方法的调用转换成对具体类型方法的调用,用策略类封装行为,再Mock这个策略类。

示例代码:

// 定义策略接口(针对被测类用到的类型)
struct FooStrategy {
    virtual void handle_int() = 0;
    virtual void handle_string() = 0;
    virtual ~FooStrategy() = default;
};

// 适配原API的策略实现
struct RealFooStrategy : public FooStrategy {
    RealFooStrategy(Interface& api) : api_(api) {}
    void handle_int() override { api_.foo<int>(); }
    void handle_string() override { api_.foo<std::string>(); }

private:
    Interface& api_;
};

// Mock策略类
struct MockFooStrategy : public FooStrategy {
    void handle_int() override { int_call_count++; }
    void handle_string() override { string_call_count++; }

    // 验证用的计数器
    int int_call_count = 0;
    int string_call_count = 0;
};

// 被测类依赖策略接口
class TestedClass {
public:
    TestedClass(FooStrategy& strategy) : strategy_(strategy) {}

    void run_tasks() {
        strategy_.handle_int();
        strategy_.handle_string();
        strategy_.handle_int();
    }

private:
    FooStrategy& strategy_;
};

// 测试代码
void test_tested_class() {
    MockFooStrategy mock_strategy;
    TestedClass tested(mock_strategy);

    tested.run_tasks();
    assert(mock_strategy.int_call_count == 2);
    assert(mock_strategy.string_call_count == 1);
}

这种方法适合被测类使用模板方法的类型固定的场景,把模板调用转化为静态的方法调用,方便Mock和验证。


内容的提问来源于stack exchange,提问作者bartop

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 14:22:27