Search code examples
c++templatesinterfacegoogletest

How to test several interface implementations with different constructors with gtest in C++?


I have an interface for which I have three implementations. I am using the TYPED_TEST from google test so that I can use the same set of tests for all implementations. I have the following Fixture.

template <typename T>
class GenericTester : public ::testing::Test {
  protected:
    T test_class;
};

I added the implementation types below.

using TestTypes = ::testing::Types<ImplementationOne, ImplementationTwo>
TYPED_TEST_SUITE(GenericDiffTester, DiffTypes);

So far, everything works fine, but now I have added another implementation. The difference between the last implementation is that its constructor requires taking a std::string as an argument, whereas the first two can be default constructed.

Now when I add this third interface, it does not compile.

using TestTypes = ::testing::Types<ImplementationOne, ImplementationTwo, ImplementationThree>
TYPED_TEST_SUITE(GenericDiffTester, DiffTypes);

Obviously, the problem is that the fixture requires test_class to be default constructible, which does not apply to ImplementationThree.

How can I initialize the templated member variable of a class depending on the provided type T? I want to default construct test_class if T is of type ImplementationOne or ImplementationTwo. Otherwise, I want to construct it as ImplementationThree with a string.

Is there a way to do it directly with Gtest without a hacky solution?


Solution

  • The easiest way is to derive from your non-default constructible class. That derived class could be default constructible:

    class TestableImplementationThree : public ImplementationThree
    {
    public:
        TestableImplementationThree() :
                ImplementationThree("dummy")
        {} 
    };
    
    

    And:

    using TestTypes = ::testing::Types<
        ImplementationOne, 
        ImplementationTwo, 
        TestableImplementationThree>;
    

    If you like to test ImplementationThree with different constructor argument - then just create and test as many testable-classes as needed.

    if you do insist on testing exactly ImplementationThree - then wrap all classes with some kind of holders. For ImplementationThree specialize its "holder" to construct it differently.

    template <typename T>
    struct Holder
    {
        T tested_object; 
    };
    
    template <>
    struct Holder<ImplementationThree>
    {
        ImplementationThree tested_object{"dummy"}; 
    };
    
    template <typename T>
    class GenericTester : public ::testing::Test,
            protected Holder<T>
    {
    };