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

如何用TDD无重复测试带特化的可变参数模板代码?

解决测试代码重复的方案

针对你的问题,我们可以通过提取通用测试逻辑或者参数化测试用例的方式,同时覆盖两种调用方式的测试,又避免重复代码。下面提供两种实用方案:

方案一:提取辅助测试函数

将重复的测试逻辑封装成辅助函数,在每个测试场景中复用,同时测试参数列表和vector版本:

#include <numeric>
#include <vector>
#include <catch2/catch_test_macros.hpp>
#include <type_traits>

// 原函数实现保持不变
template<typename Type, typename = std::enable_if_t<std::is_integral_v<Type>>>
std::size_t AddNumbers(std::vector<Type> Integers) {
    auto Sum = std::reduce(Integers.begin(), Integers.end());
    return Sum;
}

template<typename... Type, typename = std::enable_if_t<std::conjunction_v<std::is_integral<Type>...>>>
std::size_t AddNumbers(Type... Integers) {
    std::vector<std::size_t> IntegerSequence{(std::size_t)Integers...};
    return AddNumbers(IntegerSequence);
}

// 通用测试辅助函数:同时验证两种调用方式
template<typename... Args>
void VerifySum(std::size_t expected, Args... args) {
    // 测试参数列表调用
    CHECK(AddNumbers(args...) == expected);
    // 测试vector调用:自动将参数转为对应类型的vector
    std::vector<std::common_type_t<Args...>> input_vec{args...};
    CHECK(AddNumbers(input_vec) == expected);
}

// 无参数情况的单独测试函数
void VerifyEmptySum() {
    CHECK(AddNumbers() == 0);
    CHECK(AddNumbers(std::vector<int>{}) == 0);
}

TEST_CASE("Sum Test") {
    SECTION("When passed no numbers, returns 0") {
        VerifyEmptySum();
    }

    SECTION("When passed 1 number, returns the number") {
        VerifySum(1, 1);
    }

    SECTION("When passed 2 numbers, returns the sum of numbers") {
        VerifySum(3, 1, 2);
    }

    // 可快速扩展更多测试场景
    SECTION("When passed multiple positive integers, returns total sum") {
        VerifySum(15, 1, 2, 3, 4, 5);
    }
}

方案优势

  • 逻辑清晰:每个测试场景对应一个SECTION,可读性强
  • 复用性高:新增测试场景只需调用VerifySum,无需重复编写两次CHECK
  • 维护方便:修改测试逻辑只需调整辅助函数,无需逐个修改SECTION

方案二:参数化测试用例

将所有测试输入和预期结果整理成测试用例集合,遍历集合完成两种调用方式的测试,适合大量测试用例的场景:

#include <numeric>
#include <vector>
#include <catch2/catch_test_macros.hpp>
#include <type_traits>
#include <tuple>

// 原函数实现保持不变
template<typename Type, typename = std::enable_if_t<std::is_integral_v<Type>>>
std::size_t AddNumbers(std::vector<Type> Integers) {
    auto Sum = std::reduce(Integers.begin(), Integers.end());
    return Sum;
}

template<typename... Type, typename = std::enable_if_t<std::conjunction_v<std::is_integral<Type>...>>>
std::size_t AddNumbers(Type... Integers) {
    std::vector<std::size_t> IntegerSequence{(std::size_t)Integers...};
    return AddNumbers(IntegerSequence);
}

TEST_CASE("Sum Test") {
    // 定义测试用例结构:输入序列、预期结果
    struct TestCase {
        std::vector<int> inputs;
        std::size_t expected;
    };

    // 所有测试用例集合
    const std::vector<TestCase> test_cases = {
        {{}, 0},          // 空输入
        {{1}, 1},         // 单个数字
        {{1, 2}, 3},      // 两个数字
        {{2, 4, 6}, 12},  // 多个数字
        {{10, 20, 30}, 60}
    };

    for (const auto& tc : test_cases) {
        // 测试vector版本
        CHECK(AddNumbers(tc.inputs) == tc.expected);

        // 测试参数列表版本:利用std::apply将vector转为参数包
        if (tc.inputs.empty()) {
            CHECK(AddNumbers() == tc.expected);
        } else {
            std::apply([&tc](auto... args) {
                CHECK(AddNumbers(args...) == tc.expected);
            }, tc.inputs);
        }
    }
}

方案优势

  • 集中管理:所有测试用例统一维护,新增/修改只需调整集合
  • 减少冗余:无需为每个场景重复编写测试逻辑
  • 扩展性强:批量添加测试用例非常高效

额外提示

原函数实现中,参数列表版本将输入转为std::vector<std::size_t>后调用vector版本,这会导致输入的整数类型被强制转换为size_t。如果需要保留原类型的语义,可以修改参数列表版本的实现:

template<typename... Type, typename = std::enable_if_t<std::conjunction_v<std::is_integral<Type>...>>>
std::size_t AddNumbers(Type... Integers) {
    using CommonType = std::common_type_t<Type...>;
    std::vector<CommonType> IntegerSequence{static_cast<CommonType>(Integers)...};
    return AddNumbers(IntegerSequence);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 19:55:58