c++functionunit-testinggoogletest

How to auto-register test cases in a test system?


Usually in test systems when we write a new testcase we need to register the test case somewhere so that it can be called.

For example in a test system : TESTCASE(a,b){...} can map to void testcase_a_b() {...} and the test system may call each of these void testcase_a_b(), void testcase_c_d() etc. from main and hence run all the test cases.

What is the way to auto-register test cases in an executable? For example, in Google Test (just like several other test frameworks), if we call RUN_ALL_TESTS() it automatically executes all the declarations starting with TEST(a,b) etc. in the executable.

How does Google Test know about the existence of TEST(a,b) in the exe ? I am trying to understand(from a high level design perspective) what would be a simple way to implement a system like that one in C++. where a macro like TEST(a,b) automatically appends itself to the list of valid test cases, so that it can be run from main without worrying about registering it separately.


Solution

  • Generally this is done by creating global objects, which call a registration method when they are constructed. This goes against generally regarded "good practices" in C++ (see https://isocpp.org/wiki/faq/ctors#static-init-order), so you should be quite versed in these issues before attempting such an implementation.

    Regardless, this is the method googletest uses - the TEST preprocessor macro eventually boils down to this (gtest-internal.h):

    // Helper macro for defining tests.
    #define GTEST_TEST_(test_case_name, test_name, parent_class, parent_id)\
    class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) : public parent_class {\
     public:\
      GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {}\
     private:\
      virtual void TestBody();\
      static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_;\
      GTEST_DISALLOW_COPY_AND_ASSIGN_(\
          GTEST_TEST_CLASS_NAME_(test_case_name, test_name));\
    };\
    \
    ::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_case_name, test_name)\
      ::test_info_ =\
        ::testing::internal::MakeAndRegisterTestInfo(\
            #test_case_name, #test_name, NULL, NULL, \
            (parent_id), \
            parent_class::SetUpTestCase, \
            parent_class::TearDownTestCase, \
            new ::testing::internal::TestFactoryImpl<\
                GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>);\
    void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody()
    

    So, when you use this macro, a global instance of a class which calls ::testing::internal::MakeAndRegisterTestInfo with parameters corresponding to the test case.