首页 > 解决方案 > 具有类型和值参数化的 C++ 单元测试框架

问题描述

我正在寻找一个允许同时传递类型和值参数来测试功能的单元测试框架。

示例:假设我有一些模板排序功能:

template<class ITERATOR, class COMPARATOR = std::less<typename std::iterator_traits<ITERATOR>::value_type>>
void selectionSort(const ITERATOR first,const ITERATOR last, const COMPARATOR comparator = COMPARATOR ()){ ... };

template<class ITERATOR, class COMPARATOR = std::less<typename std::iterator_traits<ITERATOR>::value_type>>
void insertionSort(const ITERATOR first,const ITERATOR last, const COMPARATOR comparator = COMPARATOR ()) { ... }

我想用不同的输入类型测试这些函数:

template<class T>
vector<T> makeRandomVector(int size){ ... }

vector<int> intVec = makeRandomVector<int>(10);  //10 is vector size
someSort(intVec.begin(),intVec.end())
vector<MyCustomStruct> structVec = makeRandomVector<MyCustomStruct>(10);  
someSort(structVec.begin(),structVec.end())

makeRandomVector如果向量大小为 0、1、10 等,则使用不同的大小参数来测试这些函数。

因此,我正在寻找 C++ 单元测试框架,该框架允许创建同时接受类型 (int,MyCustomStruct) 和值参数 (0,1,10) 的测试,并对类型 x 的笛卡尔积的每个元素执行测试价值观。

标签: c++unit-testing

解决方案


一种可能的方式是 GoogleTest 的类型化测试,如下所示。请注意,这是 C++14 及以上版本的解决方案,整数参数就像您的示例一样{0, 1, 10}


方法

让我们定义以下结构Case

template<typename T, std::size_t I>
struct Case
{
    using type = T;
    static constexpr std::size_t size = I;
};

测试所有类型和大小组合的基本思想是使用这个结构来指定测试类型,如下所示:

#include <gtest/gtest.h>

template <typename T>
class TypedTest : public ::testing::Test {};

using TestTypes = ::testing::Types<Case<int, 0>, Case<int, 1>, Case<int, 10>, Case<MyCustomStruct, 0>, ...>;

TYPED_TEST_CASE(TypedTest, TestTypes);

TYPED_TEST(TypedTest, sort) 
{
    auto vec = makeRandomVector<TypeParam::type>(TypeParam::size);
    ...
}

组合学

现在我们的问题是如何简单地构造类型和大小的所有组合。我昨天刚刚回答了这个问题几乎相同的问题。在当前情况下,所有可能的{int, MyCustomStruct}和对{0, 1, 10}都由一维整数 0,1,...,5 标记,如下所示(max66 的方法也是可能的):

0 -> (0/3, 0%3) = (0,0) -> std::tuple<int,  0>
1 -> (1/3, 1%3) = (0,1) -> std::tuple<int,  1>
2 -> (2/3, 2%3) = (0,2) -> std::tuple<int, 10>
3 -> (3/3, 3%3) = (1,0) -> std::tuple<MyCustomStruct,  0>
4 -> (4/3, 4%3) = (1,1) -> std::tuple<MyCustomStruct,  1>
5 -> (5/3, 5%3) = (1,2) -> std::tuple<MyCustomStruct, 10>

3的大小在哪里{0, 1, 10}。编写与该算法进行所有可能组合的函数简单明了,如下所示。例如,Combinations<std::tuple<int, MyCustomStruct>, 0, 1, 10>等于 的类型std::tuple<Case<int,0>, Case<int,1>, Case<int,10>, Case<MyCustomStruct,0>, ...>

template<typename TupleType, typename TupleIdx, std::size_t I>
struct make_case
{
    static constexpr std::size_t N = std::tuple_size<TupleIdx>::value;

    using type = Case<typename std::tuple_element<I/N, TupleType>::type,
                               std::tuple_element<I%N, TupleIdx >::type::value>;
};

template <typename T1, typename T2, typename Is>
struct make_combinations;

template <typename TupleType, typename TupleIdx, std::size_t... Is>
struct make_combinations<TupleType, TupleIdx, std::index_sequence<Is...>>
{
    using tuples = std::tuple<typename make_case<TupleType, TupleIdx, Is>::type...>;
};

template<typename TupleTypes, std::size_t ...Is>
using Combinations = typename make_combinations
                       <TupleTypes,
                        std::tuple<std::integral_constant<std::size_t, Is>...>,
                        std::make_index_sequence<(std::tuple_size<TupleTypes>::value)*(sizeof...(Is))>>
                     ::tuples;

测试

应用这篇文章的答案,我们可以将这个元组Combinations<...>分成一个类型列表,在这里我应用 Nawaz 的简单一个。那么所有的测试都可以如下进行:

template<typename T>
struct Test;

template<typename ...T>
struct Test<std::tuple<T...>>
{
    using Types = ::testing::Types<T...>;
};

template <typename T>
class TypedTest : public ::testing::Test {};

using TestTypes = Test<Combinations<std::tuple<int, MyCustomStruct>, 0, 1, 10>>::Types;

TYPED_TEST_CASE(TypedTest, TestTypes);

TYPED_TEST(TypedTest, sort) 
{
    auto vec = makeRandomVector<TypeParam::type>(TypeParam::size);
    ...
}

推荐阅读