Search code examples
unit-testinggoogletest

GTest: fixture required when TYPE_TESTING global functions?


I want to unit-test some global templated functions using TYPED_TEST. The following code works, I just wonder whether there is way I can get rid of the test fixture, as it does not seem to be needed..

#include <gtest/gtest.h>
#include <base/mathfunctions.h>

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

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

TYPED_TEST_CASE(MinTest, Implementations);

TYPED_TEST(MinTest, ReturnsMinimumValue) 
{
   EXPECT_EQ(Base::Min<TypeParam>(-5, 5), -5);
}

Solution

  • ... as it does not seem to be needed

    It is needed. See this self-contained simplification of your unit-test source (it makes nonsense of the name ReturnsMinimumValue, but that doesn't matter):

    gtester.cpp

    #include <gtest/gtest.h>
    
    template<typename T>
    struct foo {
        static T bar() {
            return 0;
        }
    };
    
    template <class T>
    class MinTest : public testing::Test {};
    
    typedef ::testing::Types<int, float> Implementations;
    
    TYPED_TEST_CASE(MinTest, Implementations);
    
    TYPED_TEST(MinTest, ReturnsMinimumValue)
    {
        EXPECT_EQ(foo<TypeParam>::bar(),0);
    }
    int main(int argc, char **argv) {
        ::testing::InitGoogleTest(&argc, argv);
        return RUN_ALL_TESTS();
    }
    

    Compile, link and run that:

    $ g++ -Wall -Wextra -o gtester gtester.cpp -lgtest -pthread && ./gtester
    [==========] Running 2 tests from 2 test cases.
    [----------] Global test environment set-up.
    [----------] 1 test from MinTest/0, where TypeParam = int
    [ RUN      ] MinTest/0.ReturnsMinimumValue
    [       OK ] MinTest/0.ReturnsMinimumValue (0 ms)
    [----------] 1 test from MinTest/0 (0 ms total)
    
    [----------] 1 test from MinTest/1, where TypeParam = float
    [ RUN      ] MinTest/1.ReturnsMinimumValue
    [       OK ] MinTest/1.ReturnsMinimumValue (0 ms)
    [----------] 1 test from MinTest/1 (0 ms total)
    
    [----------] Global test environment tear-down
    [==========] 2 tests from 2 test cases ran. (0 ms total)
    [  PASSED  ] 2 tests.
    

    Now let's preprocess the source file, and prettify the preprocessed output (because it's not pretty):

    $ g++ -E -P gtester.cpp | clang-format > gtester.ii
    

    Look at the the last 50-odd lines of the output:

    gtester.ii

    ...
    ...
    int RUN_ALL_TESTS() __attribute__((warn_unused_result));
    inline int RUN_ALL_TESTS() { return ::testing::UnitTest::GetInstance()->Run(); }
    template <typename T> struct foo {
      static T bar() { return 0; }
    };
    template <class T> class MinTest : public testing::Test {};
    typedef ::testing::Types<int, float> Implementations;
    typedef ::testing::internal::TypeList<Implementations>::type
        gtest_type_params_MinTest_;
    template <typename gtest_TypeParam_>
    class MinTest_ReturnsMinimumValue_Test : public MinTest<gtest_TypeParam_> {
    private:
      typedef MinTest<gtest_TypeParam_> TestFixture;
      typedef gtest_TypeParam_ TypeParam;
      virtual void TestBody();
    };
    bool gtest_MinTest_ReturnsMinimumValue_registered_ __attribute__((unused)) =
        ::testing::internal::TypeParameterizedTest<
            MinTest,
            ::testing::internal::TemplateSel<MinTest_ReturnsMinimumValue_Test>,
            gtest_type_params_MinTest_>::Register("",
                                                  ::testing::internal::CodeLocation(
                                                      "gtester.cpp", 17),
                                                  "MinTest", "ReturnsMinimumValue",
                                                  0);
    template <typename gtest_TypeParam_>
    void MinTest_ReturnsMinimumValue_Test<gtest_TypeParam_>::TestBody() {
      switch (0)
      case 0:
      default:
        if (const ::testing::AssertionResult gtest_ar =
                (::testing::internal::EqHelper<(
                     sizeof(::testing::internal::IsNullLiteralHelper(
                         foo<TypeParam>::bar())) ==
                     1)>::Compare("foo<TypeParam>::bar()", "0",
                                  foo<TypeParam>::bar(), 0)))
          ;
        else
          ::testing::internal::AssertHelper(
              ::testing::TestPartResult::kNonFatalFailure, "gtester.cpp", 19,
              gtest_ar.failure_message()) = ::testing::Message();
    }
    int main(int argc, char **argv) {
      ::testing::InitGoogleTest(&argc, argv);
      return RUN_ALL_TESTS();
    }
    

    Our:

    TYPED_TEST(MinTest, ReturnsMinimumValue)
    {
        EXPECT_EQ(foo<TypeParam>::bar(),0);
    }
    

    expands to the definition of:

    template <typename gtest_TypeParam_>
    class MinTest_ReturnsMinimumValue_Test;
    

    which is derived from the template fixture:

    template <class T> class MinTest;
    

    which is derived from:

    testing::Test;
    

    from which MinTest_ReturnsMinimumValue_Test inherits:

    virtual void TestBody();
    

    and overrides it as:

    void MinTest_ReturnsMinimumValue_Test<gtest_TypeParam_>::TestBody() {
      switch (0)
      case 0:
      default:
        if (const ::testing::AssertionResult gtest_ar =
                (::testing::internal::EqHelper<(
                     sizeof(::testing::internal::IsNullLiteralHelper(
                         foo<TypeParam>::bar())) ==
                     1)>::Compare("foo<TypeParam>::bar()", "0",
                                  foo<TypeParam>::bar(), 0)))
          ;
        else
          ::testing::internal::AssertHelper(
              ::testing::TestPartResult::kNonFatalFailure, "gtester.cpp", 19,
              gtest_ar.failure_message()) = ::testing::Message();
    }
    

    which implements the expansion of:

    EXPECT_EQ(foo<TypeParam>::bar(),0);
    

    A different test framework might accomplish type-parameterised test cases differently, but this is how googletest does it.

    The polymorphic class hierarchy:

    template <typename gtest_TypeParam_>
    class MinTest_ReturnsMinimumValue_Test
        <-
    template <class T> class MinTest
        <-
    testing::Test
    

    is not generated because of any presumption the method call(s) under test are polymorphic. foo<TypeParam>::bar(), of course, is not. The polymorphic class hierarchy is generated because such is the basic design of googletest.