onqtam/doctest项目中的参数化测试详解

onqtam/doctest项目中的参数化测试详解

doctest doctest 项目地址: https://gitcode.com/gh_mirrors/doc/doctest

概述

在单元测试中,参数化测试是一种强大的技术,它允许开发者使用不同的输入数据或类型来运行相同的测试逻辑。onqtam/doctest测试框架提供了两种参数化测试的方式:基于值的参数化测试和基于类型的模板化测试。

基于值的参数化测试

目前doctest尚未提供官方的值参数化支持,但开发者可以通过以下两种方式实现类似功能。

方法一:使用辅助函数循环测试

void doChecks(int data) {
    // 使用data进行断言
}

TEST_CASE("测试名称") {
    std::vector<int> data {1, 2, 3, 4, 5, 6};

    for(auto& i : data) {
        CAPTURE(i); // 记录当前输入数据
        doChecks(i);
    }
}

缺点分析:

  1. 当遇到异常或REQUIRE断言失败时,整个测试用例会终止,剩余数据不会被测试
  2. 需要手动使用CAPTURE或INFO记录测试数据
  3. 需要自行编写数据生成代码,增加了样板代码量

方法二:使用SUBCASE实现参数化

TEST_CASE("测试名称") {
    int data;
    SUBCASE("") { data = 1; }
    SUBCASE("") { data = 2; }

    CAPTURE(data);
    // 使用data进行断言
}

缺点分析:

  1. 扩展性差,不适合大量测试数据
  2. 同样需要手动记录测试数据

改进方案:封装宏实现参数化

我们可以封装一个C++14的宏来简化操作:

#define DOCTEST_VALUE_PARAMETERIZED_DATA(data, data_container)                                  \
    static size_t _doctest_subcase_idx = 0;                                                     \
    std::for_each(data_container.begin(), data_container.end(), [&](const auto& in) {           \
        DOCTEST_SUBCASE((std::string(#data_container "[") +                                     \
                        std::to_string(_doctest_subcase_idx++) + "]").c_str()) { data = in; }  \
    });                                                                                         \
    _doctest_subcase_idx = 0

使用示例:

TEST_CASE("测试名称") {
    int data;
    std::list<int> data_container = {1, 2, 3, 4};

    DOCTEST_VALUE_PARAMETERIZED_DATA(data, data_container);

    printf("%d\n", data);
}

注意事项:

  1. 该宏不能与其他SUBCASE在同一代码块层级使用
  2. 只能在子用例中使用
  3. 数据容器必须是可迭代的(如std::vector、std::list等)

基于类型的模板化测试

当需要对多种类型实现相同的测试逻辑时,模板化测试非常有用。

直接模板化测试

TEST_CASE_TEMPLATE("有符号整数测试", T, char, short, int, long long int) {
    T var = T();
    --var;
    CHECK(var == -1);
}

定义后调用的模板化测试

TEST_CASE_TEMPLATE_DEFINE("有符号整数测试", T, test_id) {
    T var = T();
    --var;
    CHECK(var == -1);
}

TEST_CASE_TEMPLATE_INVOKE(test_id, char, short, int, long long int);
TEST_CASE_TEMPLATE_APPLY(test_id, std::tuple<float, double>);

测试命名规则: 测试用例名称会附加类型信息,如"有符号整数测试 "

类型字符串化处理

基本类型已内置字符串化支持,自定义类型需要使用TYPE_TO_STRING宏:

TYPE_TO_STRING(std::vector<int>);

注意事项:

  1. 类型不会自动去重,同一类型可能被多次实例化
  2. 不是所有类型都需要字符串化,默认会显示为"<>"
  3. 如需多类型参数化,可封装为类型对:
template <typename first, typename second>
struct TypePair {
    typedef first  A;
    typedef second B;
};

#define pairs \
    TypePair<int, char>, \
    TypePair<char, int>

TEST_CASE_TEMPLATE("多类型测试", T, pairs) {
    typedef typename T::A T1;
    typedef typename T::B T2;
    // 使用T1和T2类型
}

最佳实践建议

  1. 对于简单的值参数化测试,优先考虑辅助函数方式
  2. 当需要确保所有输入都被测试时,使用SUBCASE或封装宏的方式
  3. 对于接口或概念验证,使用模板化测试确保所有实现类型符合要求
  4. 在头文件中为常用自定义类型定义TYPE_TO_STRING,避免重复定义
  5. 考虑测试性能,避免过度参数化导致测试套件膨胀

通过合理使用参数化测试技术,可以显著提高测试代码的复用率,减少重复代码,同时确保测试覆盖全面。

doctest doctest 项目地址: https://gitcode.com/gh_mirrors/doc/doctest

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

姬虹俪Humble

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值