How to test c++ template class with multiple template parameters using gtest?
Asked Answered
H

4

20

I want to test a template class with gtest. I read about TYPED_TESTs in gtest manual and looked at the official example (samples\sample6_unittest.cc) they reference. This template from the example has only one template parameter. But, my code has two template parameters, how can I test it?

I have the following code:

// two element type
template <typename E, typename F>
class QueueNew
{
public:
    QueueNew() {}
    void Enqueue(const E& element) {}
    E* Dequeue() {}
    F size() const 
    {
        return (F)123;
    }
};

for which I wrote the test code below:

template <class E, class F>
QueueNew<E, F>* CreateQueue();

template <>
QueueNew<int, int>* CreateQueue<int, int>()
{
    return new QueueNew < int, int > ;
}
template <>
QueueNew<char, char>* CreateQueue<char, char>()
{
    return new QueueNew < char, char > ;
}

template <class E, class F>
class QueueTestNew;

template <class E>
class QueueTestNew<E, int> : public testing::Test
{
protected:
    QueueTestNew() : queue(CreateQueue<E, int>()){}
    virtual ~QueueTestNew(){ delete queue; }
    QueueNew<E, int>* const queue;
};

template <class E>
class QueueTestNew<char, E> : public testing::Test
{
protected:
    QueueTestNew() : queue(CreateQueue<char, E>()){}
    virtual ~QueueTestNew(){ delete queue; }
    QueueNew<char, E>* const queue;
};

// The list of types we want to test.
typedef ::testing::Types <char, int> Implementations;

TYPED_TEST_CASE(QueueTestNew, Implementations);

TYPED_TEST(QueueTestNew, DefaultConstructor)
{
    EXPECT_EQ(123u, this->queue->size());
}

but when building, I get the error:

error C2976: 'QueueTestNew' : too few template arguments
see declaration of 'QueueTestNew'
...

I think my test template method with gtest is wrong, so how should I do this?

Henshaw answered 1/4, 2015 at 3:1 Comment(2)
You can define a type such as a struct with multiple typedefs for each of your subtypes. Then make QueueTestNew to be type-parametrized by the that struct (nesting the additional template params).Saiva
Sorry, I don't get it.Henshaw
S
22

A trick would be to make gtest see a single type parameter, with nested types. To do this, you can define a templated structure such as:

template <typename A, typename B>
struct TypeDefinitions
{
  typedef typename A MyA;
  typedef typename B MyB;
};

Which you can pass to your typed-test fixture:

template <class T>
class QueueTestNew : public testing::Test
{
protected:
  QueueTestNew() : queue(CreateQueue<typename T::MyA, typename T::MyB>()){}
  virtual ~QueueTestNew(){ delete queue; }
  QueueNew<typename T::MyA, typename T::MyB>* const queue;
};

// The list of types we want to test.
typedef ::testing::Types <TypeDefinitions<char,char>,
                          TypeDefinitions<int,int> > Implementations;

TYPED_TEST_CASE(QueueTestNew, Implementations);

TYPED_TEST(QueueTestNew, DefaultConstructor)
{
  typename TypeParam::MyA someA; // if you need access to the subtypes in the test itself

  EXPECT_EQ(123u, this->queue->size());
}
Saiva answered 1/4, 2015 at 3:40 Comment(1)
Oh bravo. got it, thx very much! Template and typename are very strong!Henshaw
S
12

An example that might also work, and doesn't require a custom struct, is using std::tuples

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

typedef ::testing::Types <std::tuple<float64_t, float32_t, int16>, std::tuple<int64, int8, float32_t> > Implementations;

TYPED_TEST_CASE(TestThreeParams, Implementations);

TYPED_TEST(TestThreeParams, MaximumTest)
{
    using A = std::tuple_element<0, decltype(TypeParam())>::type;
    using B = std::tuple_element<1, decltype(TypeParam())>::type;
    using C = std::tuple_element<2, decltype(TypeParam())>::type;

    bool test = (Max<A, B, C>(A(-5), B(2), C(5)) == 5);
    EXPECT_TRUE(test);
}
Selfcommand answered 6/9, 2018 at 15:34 Comment(0)
F
0

Not enough reputation to comment, but contrary to the accepted answer with gcc 11.4.0, C++17, Ubuntu 22.04, I had to do the following instead for the struct definition:

template <typename A, typename B>
struct TypeDefinitions {
    typedef A MyA;
    typedef B MyB;
};

The final fixture would look the same but the usage of a subtype within a TYPED_TEST needed to be as follows:

TYPED_TEST(QueueTestNew, DefaultConstructor)
{
  typedef typename TypeParam::MyA someA; // if you need access to the subtypes in the test itself

  EXPECT_EQ(123u, this->queue->size());
}
Feathered answered 16/5, 2024 at 19:22 Comment(0)
M
0

I liked Ben's answer. I massaged it a bit to work for me and here's how the code looks like:

template <typename Types>
class QueueTest : public testing::Test
{
    using E = std::tuple_element_t<0, Types>;
    using F = std::tuple_element_t<1, Types>;
public:
    const QueueNew<E, F> *queue;
};

using Implementations = ::testing::Types<
    std::tuple<char, char>,
    std::tuple<int, int>,
    std::tuple<char, int>
>;
TYPED_TEST_SUITE(QueueTest, Implementations);

TYPED_TEST(QueueTest, DefaultConstructor)
{
    EXPECT_EQ(123U, this->queue->size());
}

Note that TYPED_TEST_CASE isn't provided by Google Tests anymore and so I used TYPED_TEST_SUITE instead.

Martens answered 11/7, 2024 at 16:22 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.