首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在Google中测试具有不同模板的多个模板类的相同行为?

如何在Google中测试具有不同模板的多个模板类的相同行为?
EN

Stack Overflow用户
提问于 2019-04-28 16:56:08
回答 1查看 1.6K关注 0票数 5

我正在C++ 17中练习排序算法,并实现了以下单元测试(下面的编译和所有测试都是绿色的):

代码语言:javascript
复制
template <typename T>
class SortingmethodTest : public ::testing::Test
{
protected:   
    T sortingmethod;

    static constexpr int amount_test_data[7] = {0, 4, 8, 10, 256, 1000, 1234};
};

using sortingmethods = ::testing::Types<STLSort<int>,
                                         InsertionSort<int>,
                                         ShellSort<int>,
                                         MergeSort<int>,
                                         OptimizedMergeSort<int>,
                                         QuickSort<int>>;

TYPED_TEST_SUITE(SortingmethodTest, sortingmethods);

TYPED_TEST(SortingmethodTest, sort)
{
    for (const auto& amount : this->amount_test_data)
    {
        Sortvector<int> test(amount);
        test.vul_random_zonder_dubbels(); // Fills the vector

        this->sortingmethod(test); // operator() of the sortmethod used (STLSort, InsertionSort, ...) sorts the vector

        ASSERT_TRUE(test.is_range());
        ASSERT_TRUE(test.is_gesorteerd());
        ASSERT_TRUE(std::is_sorted(test.begin(), test.end()));
    }
}

TYPED_TEST(SortingmethodTest, sort_reverse)
{
    // ...
}

TYPED_TEST(SortingmethodTest, sort_already_sorted)
{
    // ...
}

TYPED_TEST(SortingmethodTest, sort_empty)
{
    // ...
}

我想对其他类型的ints重复相同的测试。

代码语言:javascript
复制
STLSort<int>,
InsertionSort<int>,
ShellSort<int>,
MergeSort<int>,
OptimizedMergeSort<int>,
QuickSort<int>

STLSort<double>,
InsertionSort<double>,
ShellSort<double>,
MergeSort<double>,
OptimizedMergeSort<double>,
QuickSort<double>

STLSort<CustomType>,
InsertionSort<CustomType>,
ShellSort<CustomType>,
MergeSort<CustomType>,
OptimizedMergeSort<CustomType>,
QuickSort<CustomType>

...

我如何在C++中尽可能干净地使用谷歌测试并尽可能多地重用?我迷失在类型化测试和类型参数化测试的丛林中。1:我什么时候应该使用其中一种?

向您致以亲切的问候,

马滕

1

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-05-02 07:35:25

通常令人沮丧的是,googletest到目前为止并没有让我们更多地利用现代C++来使测试代码简洁,特别是对于模板的测试。但是通过v1.8.x (目前的发布系列),googletest一直致力于C++98兼容性,这就是原因所在。即将发布的1.9.x版本将转向C++11兼容性,我们可以期待一个更强大的API。

尽管如此,现在可以编写相当简洁和直观的googletest代码来完成您想要的事情:也就是,为一个模板参数的不同值进行单元测试一致性模板。

有不止一种方法可以做到。下面是其中一个使用类型参数化测试的工作示例。

我们将有一组三个模板

代码语言:javascript
复制
template<typename T> struct (AA|BB|CC) {...};

其中每一个都提供(至少)接口:

代码语言:javascript
复制
Name::Name(T const & u);
Name::operator int() const;
Name Name::operator+(Name const & u) const;
Name & Name::operator+=(Name const & u);
Name Name::operator-(Name const & u) const;
Name & Name::operator-=(Name const & u);

表示Name = (AA|BB|CC)。我们希望对每个(AA|BB|CC)的每个接口进行单元测试,每个接口实例化为六种类型中的每一种:

代码语言:javascript
复制
char, int, float, AA<char>, BB<int>, CC<float>

因此,需要测试的实例化有18个:

代码语言:javascript
复制
AA<char>, AA<int>, AA<float>, AA<AA<char>>, AA<BB<int>>, AA<CC<float>>
BB<char>, BB<int>, BB<float>, BB<AA<char>>, BB<BB<int>>, BB<CC<float>>
CC<char>, CC<int>, CC<float>, CC<AA<char>>, CC<BB<int>>, CC<CC<float>>

为了保持简短,我们将只实现两个通用测试。对于任何实例化测试类型的对象abc

  • a = b + c; b += c之后,然后是a == b
  • 给定b != c,在a = b - c; c -= b之后,然后是a != c

(至少,只要操作不溢出或丢失精度,这些属性就应该保持不变,这是我要避免的)。

所以我们预计总共会有36次测试。

对于这个例子,我不关心AABBCC是什么,除了它们的公共接口之外,所以我将从一个单一的模型中得到它们,如下所示:

some_types.h

代码语言:javascript
复制
#pragma once

#include <type_traits>

namespace detail {
    template<typename T>
    struct bottom_type {
        using type = T;
    };

    template<template<typename ...> class C, typename ...Ts>
    struct bottom_type<C<Ts...>> {
        using type = typename C<Ts...>::type;
    };
}

template<typename T>
using bottom_t = typename detail::bottom_type<T>::type;

template<
    typename T,
    typename Enable = std::enable_if_t<std::is_arithmetic_v<bottom_t<T>>>
>
struct model
{
    using type = bottom_t<T>;

    model() = default;
    model(model const &) = default;
    model(T const & t)
    : _t{t}{}

    operator type() const { return _t; }

    auto operator+(model const & u) const {
        return _t + u;
    }

    auto & operator+=(model const & u) {
        _t += u;
        return *this;
    }

    auto operator-(model const & u ) const {
        return _t - u;
    }

    auto & operator-=(model const & u ) {
        _t -= u;
        return *this;
    }

protected:
    type _t = 0;
};

template<typename T> struct AA : model<T>{ using model<T>::model; };
template<typename T> struct BB : model<T>{ using model<T>::model; };
template<typename T> struct CC : model<T>{ using model<T>::model; };

下面是我的googletest代码:

main.cpp

代码语言:javascript
复制
#include <array>
#include <algorithm>
#include <random>
#include <type_traits>
#include <limits>
#include <gtest/gtest.h>
#include "some_types.h"

template<typename T>
struct fixture : public ::testing::Test
{
protected:

    template<typename U>
    static auto const & test_data() {
        using type = bottom_t<U>;
        static std::array<type,1000> data;
        static bool called;
        if (!called) {
            std::default_random_engine gen;
            auto low = std::numeric_limits<type>::min() / 2;
            auto high = std::numeric_limits<type>::max() / 2;
            auto dist = [&low,&high](){
                if constexpr (std::is_floating_point_v<type>) {
                    return std::uniform_real_distribution<type>(low,high);
                } else {
                    return std::uniform_int_distribution<type>(low,high);
                }
            }();
            std::generate(
                data.begin(),data.end(),[&dist,&gen](){ return dist(gen); });
            called = true;
        }
        return data;
    }
};


template<template<typename> class C, typename ...Ts>
using test_types = ::testing::Types<C<Ts>...>;

using AA_test_types = test_types<AA,char,int,float,AA<char>,BB<int>,CC<float>>;
using BB_test_types = test_types<BB,char,int,float,AA<char>,BB<int>,CC<float>>;
using CC_test_types = test_types<CC,char,int,float,AA<char>,BB<int>,CC<float>>;

TYPED_TEST_SUITE_P(fixture);

TYPED_TEST_P(fixture, addition)
{
    using wrapped_type = typename TypeParam::type;
    auto const & data = this->template test_data<wrapped_type>();
    auto fi = data.begin(); auto ri = data.rbegin();
    for ( ; fi != ri.base(); ++fi, ++ri)
    {
        TypeParam lhs{*fi}, rhs{*ri};
        auto sum = lhs + rhs;
        lhs += rhs;
        ASSERT_EQ(lhs,sum);
    }
}

TYPED_TEST_P(fixture, subtraction)
{
    using wrapped_type = typename TypeParam::type;
    auto const & data = this->template test_data<wrapped_type>();
    auto fi = data.begin(); auto ri = data.rbegin();
    for ( ; fi != ri.base(); ++fi, ++ri) {
        TypeParam lhs{*fi}, rhs{*ri};
        if (lhs != rhs) {
            auto diff = lhs - rhs;
            rhs -= lhs;
            ASSERT_NE(rhs,diff);
        }
    }
}

REGISTER_TYPED_TEST_SUITE_P(fixture,addition,subtraction);
INSTANTIATE_TYPED_TEST_SUITE_P(AA_tests, fixture, AA_test_types);
INSTANTIATE_TYPED_TEST_SUITE_P(BB_tests, fixture, BB_test_types);
INSTANTIATE_TYPED_TEST_SUITE_P(CC_tests, fixture, CC_test_types);

int main(int argc, char **argv) {
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

让我们谈谈兴趣所在:-

代码语言:javascript
复制
template<template<typename> class C, typename ...Ts>
using test_types = ::testing::Types<C<Ts>...>;

在这里,我将test_types作为::testing::Types<SomeType...>列表的模板别名,其中SomeType将是测试中的一个模板的实例化。碰巧,我的模板AABBCC (和您的模板一样)都是这样的:

代码语言:javascript
复制
template<typename T> class;

所以我想让test_types成为:

代码语言:javascript
复制
::testing::Types<C<Ts>...>

然后,我定义了三个具体类型的别名:

代码语言:javascript
复制
using AA_test_types = test_types<AA,char,int,float,AA<char>,BB<int>,CC<float>>;
using BB_test_types = test_types<BB,char,int,float,AA<char>,BB<int>,CC<float>>;
using CC_test_types = test_types<CC,char,int,float,AA<char>,BB<int>,CC<float>>;

分别相当于:

代码语言:javascript
复制
::testing::Types<AA<char>, AA<int>, AA<float>, AA<AA<char>>, AA<BB<int>>, AA<CC<float>>>;
::testing::Types<BB<char>, BB<int>, BB<float>, BB<AA<char>>, BB<BB<int>>, BB<CC<float>>>;
::testing::Types<CC<char>, CC<int>, CC<float>, CC<AA<char>>, CC<BB<int>>, CC<CC<float>>>;

然后用模板夹具fixture定义了一个类型参数化的测试套件.

代码语言:javascript
复制
TYPED_TEST_SUITE_P(fixture);

然后我定义了我的两种类型参数化测试模式。

代码语言:javascript
复制
TYPED_TEST_P(fixture, addition)
{
    using wrapped_type = typename TypeParam::type;
    auto const & data = this->template test_data<wrapped_type>();
    auto fi = data.begin(); auto ri = data.rbegin();
    for ( ; fi != ri.base(); ++fi, ++ri)
    {
        TypeParam lhs{*fi}, rhs{*ri};
        auto sum = lhs + rhs;
        lhs += rhs;
        ASSERT_EQ(lhs,sum);
    }
}

TYPED_TEST_P(fixture, subtraction)
{
    using wrapped_type = typename TypeParam::type;
    auto const & data = this->template test_data<wrapped_type>();
    auto fi = data.begin(); auto ri = data.rbegin();
    for ( ; fi != ri.base(); ++fi, ++ri) {
        TypeParam lhs{*fi}, rhs{*ri};
        if (lhs != rhs) {
            auto diff = lhs - rhs;
            rhs -= lhs;
            ASSERT_NE(rhs,diff);
        }
    }
}

然后,我将这两种模式都注册到fixture的每个实例化中。

代码语言:javascript
复制
REGISTER_TYPED_TEST_SUITE_P(fixture,addition,subtraction);

然后,为测试类型列表(AA|BB|CC)_tests fixture分别创建3个名为(AA|BB|CC)_test_types的实例化:

代码语言:javascript
复制
INSTANTIATE_TYPED_TEST_SUITE_P(AA_tests, fixture, AA_test_types);
INSTANTIATE_TYPED_TEST_SUITE_P(BB_tests, fixture, BB_test_types);
INSTANTIATE_TYPED_TEST_SUITE_P(CC_tests, fixture, CC_test_types);

就是这样。汇编和链接:

代码语言:javascript
复制
$ g++ -std=c++17 -Wall -Wextra -pedantic -o gtester main.cpp -lgtest -pthread

运行:

代码语言:javascript
复制
./gtester
[==========] Running 36 tests from 18 test suites.
[----------] Global test environment set-up.
[----------] 2 tests from AA_tests/fixture/0, where TypeParam = AA<char>
[ RUN      ] AA_tests/fixture/0.addition
[       OK ] AA_tests/fixture/0.addition (0 ms)
[ RUN      ] AA_tests/fixture/0.subtraction
[       OK ] AA_tests/fixture/0.subtraction (1 ms)
[----------] 2 tests from AA_tests/fixture/0 (1 ms total)

[----------] 2 tests from AA_tests/fixture/1, where TypeParam = AA<int>
[ RUN      ] AA_tests/fixture/1.addition
[       OK ] AA_tests/fixture/1.addition (0 ms)
[ RUN      ] AA_tests/fixture/1.subtraction
[       OK ] AA_tests/fixture/1.subtraction (0 ms)
[----------] 2 tests from AA_tests/fixture/1 (0 ms total)
...
...
...
[----------] 2 tests from CC_tests/fixture/4, where TypeParam = CC<BB<int> >
[ RUN      ] CC_tests/fixture/4.addition
[       OK ] CC_tests/fixture/4.addition (0 ms)
[ RUN      ] CC_tests/fixture/4.subtraction
[       OK ] CC_tests/fixture/4.subtraction (0 ms)
[----------] 2 tests from CC_tests/fixture/4 (0 ms total)

[----------] 2 tests from CC_tests/fixture/5, where TypeParam = CC<CC<float> >
[ RUN      ] CC_tests/fixture/5.addition
[       OK ] CC_tests/fixture/5.addition (0 ms)
[ RUN      ] CC_tests/fixture/5.subtraction
[       OK ] CC_tests/fixture/5.subtraction (0 ms)
[----------] 2 tests from CC_tests/fixture/5 (0 ms total)

[----------] Global test environment tear-down
[==========] 36 tests from 18 test suites ran. (4 ms total)
[  PASSED  ] 36 tests.
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55892577

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档