Home > Net >  Templated Googletest for enums
Templated Googletest for enums

Time:09-13

The C Googletest is fairly well documented as to how to write templated tests for standard types. What I need, however, is write tests where the template is an enum and the implementations are specific values of that enum. I can't figure out how to make this work with the tools available (or whether it's even possible).

Copying the examples, I've been trying something along the lines of:

enum Dinosaur { Trex = 0, Stegosaur = 1, Triceratops = 2 };

using MyDinosaurs = ::testing::Types<
    Dinosaur::Trex, 
    Dinosaur::Stegosaur, 
    Dinosaur::Triceratops>; // This line does not compile, since Dinosaur::Trex etc aren't actually types


template <Dinosaur Dinosaur_T>
class DinosaurTest : public ::testing::Test 
{ 

private:
    DinosaurFactory<Dinosaur_T> m_dinosaurFactory;
};

TYPED_TEST_SUITE(DinosaurTest, MyDinosaurs);

TYPED_TEST_P(DinosaurTest, DinosaurEnumTest) 
{ 
    ASSERT_NE(nullptr, m_dinosaurFactory.Create()); 
}

REGISTER_TYPED_TEST_SUITE_P(DinosaurTest, DinosaurEnumTest);

INSTANTIATE_TYPED_TEST_SUITE_P(MyDinosaurTestSuite, DinosaurTest, MyDinosaurs); // This line then also does not compile, since 'MyDinosaurs' isn't a valid list of types

How can I make a test in this fashion work for enums?

CodePudding user response:

You can use std::integral_constant to actually provide type instead of value:

using MyDinosaurs = ::testing::Types<
std::integral_constant<decltype(Dinosaur::Trex),Dinosaur::Trex>, 
std::integral_constant<decltype(Dinosaur::Trex), Dinosaur::Stegosaur>,
std::integral_constant<decltype(Dinosaur::Trex), Dinosaur::Triceratops>>;

You can call ::value on your std::integral_constant to actually get the enum value so, modify your fixture to:

template <typename T>
class DinosaurTest : public ::testing::Test 
{ 

public:
    DinosaurFactory<T::value> m_dinosaurFactory; // T::value is your enum type
}

With these modifications, you are set. You can check complete example based on your problem here: https://godbolt.org/z/97eszrMsK

Note: I'm not sure if there is another way, I haven't found it.

You can also use this approach instead of std::integral_constant:

template <Dinosaur N>
struct EnumValue{
    static const Dinosaur value = N;
};

using MyDinosaurs = ::testing::Types<
    EnumValue<Dinosaur::Trex>, 
    EnumValue<Dinosaur::Stegosaur>,
    EnumValue<Dinosaur::Triceratops>>;

The rest is the same as above.

CodePudding user response:

Are you sure you want to have a type as the test parameter and not a value?

It seems to me that you need to test the DinosaurFactory given different Dinosaur types from your enum. So perhaps your DinosaurFactory should not be a template but it should just take the type as a parameter?

If that's the case, you can use a Parameterized Test instead of Typed tests as below:

enum class Dinosaur { Trex = 0, Stegosaur = 1, Triceratops = 2 };

class CDinosaur {
 public:
  CDinosaur(Dinosaur type) : m_type(type) {}
  Dinosaur m_type;
};

class DinosaurFactory {
 public:
  CDinosaur* Create(Dinosaur type) { return new CDinosaur(type); }
};

class DinosaurTest : public testing::TestWithParam<Dinosaur> {
 public:
  DinosaurFactory m_dinosaurFactory;
};

// This will create multiple tests.
TEST_P(DinosaurTest, DinosaurEnumTest) {
  auto type = GetParam();
  auto actual = m_dinosaurFactory.Create(type);
  ASSERT_NE(nullptr, actual);
  if (actual != nullptr) {
    delete actual;
  }
}

INSTANTIATE_TEST_SUITE_P(DinosaurTestWithFactory, DinosaurTest,
                         testing::Values(Dinosaur::Trex, Dinosaur::Stegosaur,
                                         Dinosaur::Triceratops));

Live example: https://godbolt.org/z/MdjbrTfEP

Side notes:

  1. Use enum class instead of enum.
  2. Don't forget to delete the pointer resulting from m_dinosaurFactory.Create or use a unique_ptr.
  • Related