文档开发者中心
我的

GoogleTest常用宏源码探究(一)

背景简介


Google Test简称Gtest,是Google开发的开源的C++单元测试框架。


初步阅读Gtest源码就会发现,Gtest中大量使用宏,宏的使用比函数更加灵活,一句看似简单的宏完全展开后已经面目全非,它可能在背后完成了许多复杂的功能。Gtest的易用性很多也来自于宏的使用。


RUN_ALL_TEST()宏


该宏是Gtest的启动宏,通常在main中调用,整个测试框架就开始运行。


该宏的定义为:


#define RUN_ALL_TESTS()
 (::testing::UnitTest::GetInstance()->Run())


该宏实现并获取了一个UnitTest单例对象,并调用其Run方法。


UnitTest是测试框架的主体对象,生存周期为整个测试程序。


其Run()方法定义为(节选)


int UnitTest::Run() {

 //初始化配置相关,已省略
...
  return internal::HandleExceptionsInMethodIfSupported(

     impl(),

     &internal::UnitTestImpl::RunAllTests,

    'auxiliary test code (environments or event listeners)') ? 0 : 1;

}


该方法初始化一些配置,并且调用HandleExceptionsInMethodIfSupported方法,该方法返回一个bool表示测试的执行结果。


HandleExceptionsInMethodIfSupported是一个模板函数,其声明如下:


template <class T, typename Result>

Result HandleExceptionsInMethodIfSupported(

   T* object, Result (T::*method)(), const char* location) ;


HandleExceptionsInMethodIfSupported的定义不再展开,简而言之,就是调用第二个参数的方法,也就是UnitTestImpl::RunAllTests()。


UnitTestImpl::RunAllTests()基本定义了整个测试的大体流程,完整定义如下:


// Runs all tests in this UnitTest object, prints the result, and

// returns true if all tests are successful.  If any exception is

// thrown during a test, the test is considered to be failed, but the

// rest of the tests will still be run.

//

// When parameterized tests are enabled, it expands and registers

// parameterized tests first in RegisterParameterizedTests().

// All other functions called from RunAllTests() may safely assume that

// parameterized tests are ready to be counted and run.

bool UnitTestImpl::RunAllTests() {

// True if Google Test is initialized before RUN_ALL_TESTS() is called.

const bool gtest_is_initialized_before_run_all_tests = GTestIsInitialized();


// Do not run any test if the --help flag was specified.

if (g_help_flag)

  return true;


// Repeats the call to the post-flag parsing initialization in case the

// user didn't call InitGoogleTest.

PostFlagParsingInit();


// Even if sharding is not on, test runners may want to use the

// GTEST_SHARD_STATUS_FILE to query whether the test supports the sharding

// protocol.

internal::WriteToShardStatusFileIfNeeded();


// True if we are in a subprocess for running a thread-safe-style

// death test.

bool in_subprocess_for_death_test = false;


#if GTEST_HAS_DEATH_TEST

 in_subprocess_for_death_test =

     (internal_run_death_test_flag_.get() != nullptr);

# if defined(GTEST_EXTRA_DEATH_TEST_CHILD_SETUP_)

if (in_subprocess_for_death_test) {

  GTEST_EXTRA_DEATH_TEST_CHILD_SETUP_();

 }

# endif // defined(GTEST_EXTRA_DEATH_TEST_CHILD_SETUP_)

#endif // GTEST_HAS_DEATH_TEST


const bool should_shard = ShouldShard(kTestTotalShards, kTestShardIndex,

                                       in_subprocess_for_death_test);


// Compares the full test names with the filter to decide which

// tests to run.

const bool has_tests_to_run = FilterTests(should_shard

                                             ? HONOR_SHARDING_PROTOCOL

                                             : IGNORE_SHARDING_PROTOCOL) > 0;


// Lists the tests and exits if the --gtest_list_tests flag was specified.

if (GTEST_FLAG(list_tests)) {

  // This must be called *after* FilterTests() has been called.

  ListTestsMatchingFilter();

  return true;

 }


 random_seed_ = GTEST_FLAG(shuffle) ?

     GetRandomSeedFromFlag(GTEST_FLAG(random_seed)) : 0;


// True if at least one test has failed.

bool failed = false;


 TestEventListener* repeater = listeners()->repeater();


 start_timestamp_ = GetTimeInMillis();

 repeater->OnTestProgramStart(*parent_);


// How many times to repeat the tests?  We don't want to repeat them

// when we are inside the subprocess of a death test.

const int repeat = in_subprocess_for_death_test ? 1 : GTEST_FLAG(repeat);

// Repeats forever if the repeat count is negative.

const bool gtest_repeat_forever = repeat < 0;

for (int i = 0; gtest_repeat_forever || i != repeat; i++) {

  // We want to preserve failures generated by ad-hoc test

  // assertions executed before RUN_ALL_TESTS().

   ClearNonAdHocTestResult();


  const TimeInMillis start = GetTimeInMillis();


  // Shuffles test suites and tests if requested.

  if (has_tests_to_run && GTEST_FLAG(shuffle)) {

    random()->Reseed(static_cast<UInt32>(random_seed_));

     // This should be done before calling OnTestIterationStart(),

    // such that a test event listener can see the actual test order

    // in the event.

    ShuffleTests();

   }


  // Tells the unit test event listeners that the tests are about to start.

   repeater->OnTestIterationStart(*parent_, i);


  // Runs each test suite if there is at least one test to run.

  if (has_tests_to_run) {

    // Sets up all environments beforehand.

     repeater->OnEnvironmentsSetUpStart(*parent_);

    ForEach(environments_, SetUpEnvironment);

     repeater->OnEnvironmentsSetUpEnd(*parent_);


    // Runs the tests only if there was no fatal failure or skip triggered

    // during global set-up.

    if (Test::IsSkipped()) {

      // Emit diagnostics when global set-up calls skip, as it will not be

      // emitted by default.

       TestResult& test_result =

           *internal::GetUnitTestImpl()->current_test_result();

      for (int j = 0; j < test_result.total_part_count(); ++j) {

        const TestPartResult& test_part_result =

             test_result.GetTestPartResult(j);

        if (test_part_result.type() == TestPartResult::kSkip) {

          const std::string& result = test_part_result.message();

          printf('%s', result.c_str());

         }

       }

      fflush(stdout);

     } else if (!Test::HasFatalFailure()) {

      for (int test_index = 0; test_index < total_test_suite_count();

            test_index++) {

        GetMutableSuiteCase(test_index)->Run();

       }

     }


    // Tears down all environments in reverse order afterwards.

     repeater->OnEnvironmentsTearDownStart(*parent_);

    std::for_each(environments_.rbegin(), environments_.rend(),

                   TearDownEnvironment);

     repeater->OnEnvironmentsTearDownEnd(*parent_);

   }


   elapsed_time_ = GetTimeInMillis() - start;


  // Tells the unit test event listener that the tests have just finished.

   repeater->OnTestIterationEnd(*parent_, i);


  // Gets the result and clears it.

  if (!Passed()) {

     failed = true;

   }


  // Restores the original test order after the iteration.  This

  // allows the user to quickly repro a failure that happens in the

  // N-th iteration without repeating the first (N - 1) iterations.

  // This is not enclosed in 'if (GTEST_FLAG(shuffle)) { ... }', in

  // case the user somehow changes the value of the flag somewhere

  // (it's always safe to unshuffle the tests).

  UnshuffleTests();


  if (GTEST_FLAG(shuffle)) {

    // Picks a new random seed for each iteration.

     random_seed_ = GetNextRandomSeed(random_seed_);

   }

 }


 repeater->OnTestProgramEnd(*parent_);


if (!gtest_is_initialized_before_run_all_tests) {

  ColoredPrintf(

       COLOR_RED,

      'IMPORTANT NOTICE - DO NOT IGNORE:'

      'This test program did NOT call ' GTEST_INIT_GOOGLE_TEST_NAME_

      '() before calling RUN_ALL_TESTS(). This is INVALID. Soon ' GTEST_NAME_

      ' will start to enforce the valid usage. '

      'Please fix it ASAP, or IT WILL START TO FAIL.'); // NOLINT

#if GTEST_FOR_GOOGLE_

  ColoredPrintf(COLOR_RED,

                'For more details, see http://wiki/Main/ValidGUnitMain.');

#endif // GTEST_FOR_GOOGLE_

 }


return !failed;

}


其中浅蓝色区域的代码是一个for循环,执行逐个Test Suite测试。而深蓝色部分是一个内层for循环,执行当前Test Suite内的每个Test Case测试。


其中repeater是一个指针,指向TestEventListener接口类。TestEventListener接口类有一系列OnXXX()回调方法。repeater在不同的执行阶段去调用这些OnXXX()方法。


我们可以看到这些回调函数的执行顺序:


基本配置完成后:

OnTestProgramStart()

  每个TestSuite内:{

OnTestIterationStart()

如果有测试需要执行{

OnEnvironmentsSetUpStart()

逐个环境搭建完成

OnEnvironmentsSetUpEnd()

          每个TestCase内{

                  执行测试

          }

OnEnvironmentsTearDownStart()

逐个环境拆卸完成

OnEnvironmentsTearDownEnd()

}

OnTestIterationEnd()

}

OnTestProgramEnd()


对于测试用例的执行时如何在其中开展的本文不继续追踪。


TEST(),TEST_F()宏


TEST_F()和TEST()都是Gtest的中的主要宏,用于定义一项测试。他们两都有两个参数,使用的时候看起来好像差不多,都是定义测试的名称和类型,但是其实是具有不同意义的。在宏定义中参数名分别为TEST(test_suite_name , test_name)和TEST_F(test_fixture , test_name)。


TEST(test_suite_name , test_name)是用于定义一项测试,第一个参数是suite的名字,一个suite通常对应一个测试对象,而第二个参数是case的名字,也就是一条测试用例。在官方文档中强调了test_suite_name的命名规范,必须以Test结尾。


TEST_F(test_fixture , test_name)是使用fixture来定义一项测试,fixture其实也是一个suite,与suite不同的是它需要我们提前定义这个fixture类,也就是说这里的test_fixture应当是我们已经定义的一个类名,这个类名也是我们的test_suite_name。fixture类应当继承testing::Test类,并重写SetUp、TearDown方法,这两个方法会分别在fixture执行测试的前、后执行。


以下是两个宏的第一级展开:


#define GTEST_TEST(test_suite_name, test_name)              

GTEST_TEST_(test_suite_name, test_name, ::testing::Test,

            ::testing::internal::GetTestTypeId())


// Define this macro to 1 to omit the definition of TEST(), which

// is a generic name and clashes with some other libraries.

#if !GTEST_DONT_DEFINE_TEST

#define TEST(test_suite_name, test_name) GTEST_TEST(test_suite_name, test_name)

#endif



#define TEST_F(test_fixture, test_name)

 GTEST_TEST_(test_fixture, test_name, test_fixture,

             ::testing::internal::GetTypeId<test_fixture>())


可以看到TEST()和TEST_F()宏最终都被展开为GTEST_TEST_()宏,其中主要是第三个参数不同,TEST_F()是用的我们定义的fixture类,而TEST()是用的fixture的基类testing::Test类。在testing::Test类中,SetUp和TearDown都是空函数。所以,TEST_F()和TEST()的主要区别就在于:使用TEST_F(),我们需要自定义一个fixture类继承testing::Test类,来重写回调函数SetUp()和TearDown(),而TEST()则不具有该功能,其余二者都一样。


我们继续追踪GTEST_TEST_()的宏展开,在gtest/internal/gtest-internal.h中,其定义如下:


#define GTEST_TEST_(test_suite_name, test_name, parent_class, parent_id)      

class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)                    

     : public parent_class {                                                

  public:                                                                    

  GTEST_TEST_CLASS_NAME_(test_suite_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_suite_name,  

                                                          test_name));      

 };                                                                          

                                                                             

 ::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_suite_name,          

                                                   test_name)::test_info_ =  

     ::testing::internal::MakeAndRegisterTestInfo(                          

         #test_suite_name, #test_name, nullptr, nullptr,                    

        ::testing::internal::CodeLocation(__FILE__, __LINE__), (parent_id),

         ::testing::internal::SuiteApiResolver<                              

             parent_class>::GetSetUpCaseOrSuite(__FILE__, __LINE__),        

         ::testing::internal::SuiteApiResolver<                              

             parent_class>::GetTearDownCaseOrSuite(__FILE__, __LINE__),      

         new ::testing::internal::TestFactoryImpl<GTEST_TEST_CLASS_NAME_(    

             test_suite_name, test_name)>);                                  

void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody()


可以看到,在这里,GTEST_TEST_()宏被展开成了一大段类定义,这个类继承了我们定义的fixture类(或Test类,当使用TEST()时),这其中又使用了一些宏。我们继续一一展开。

首先GTEST_TEST_CLASS_NAME_是一个很简单的宏,定义如下:


 #define GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)

test_suite_name##_##test_name##_Test


该宏的功能只是完成了一个字符串拼接的功能,将test_suite_name和test_name以’_’连接到一起,并在末尾添加”_Test”,它将作为类名。


GTEST_ATTRIBUTE_UNUSED是一个批注宏,用于声明一个变量或实例未使用。这类宏的展开会视c++编译器的类型而定,它的作用是提供信息给编译器,而我们并不需要关心。

GTEST_DISALLOW_COPY_AND_ASSIGN_()宏不允许使用复制构造函数和使用等号赋值。这个宏应当在private:中使用,其定义如下:


#define GTEST_DISALLOW_COPY_AND_ASSIGN_(type)

type(type const &) = delete;

GTEST_DISALLOW_ASSIGN_(type)

#define GTEST_DISALLOW_ASSIGN_(type)

void operator=(type const &) = delete


到这里,所有的宏都被展开了。我们观察GTEST_TEST_CLASS_NAME_这个类的定义。该类的主要成员是一个私有的TestBody()虚函数,一个TestInfo常量指针,并在类外对TestInfo指针赋值,TestBody()的函数体则是我们定义的TEST()或TEST_F()的函数体。我们看一下给TestInfo* 赋值的MakeAndRegisterTestInfo函数,它的功能是创建并注册一个TestInfo对象。它有7个参数,分别是


GTEST_API_ TestInfo* MakeAndRegisterTestInfo(


const char* test_suite_name,

suite名

const char* name,

case名

const char* type_param,

测试参数的类型,没有则为null

const char* value_param,

测试参数的值,没有则为null

CodeLocation code_location,

测试定义的代码的位置

TypeId fixture_class_id,

fixture类的id

SetUpTestSuiteFunc set_up_tc,

suite的SetUp函数指针

TearDownTestSuiteFunc tear_down_tc,

suite的TearDown函数指针

TestFactoryBase* factory

);

创建测试类对象的工厂


总结以上分析,简单来说就是:TEST_F()和TEST()宏展开内容实际上是一个类定义,以及完成该类的注册。在TEST_F()中该类继承我们定义的fixture类(该类继承Test类),而TEST()中该类直接继承Test类。我们定义的函数体,是这个类的成员函数TestBody()的函数体。


TEST_P()宏


TEST_P()定义一项带参数的测试,使用TEST_P()可是使用多种不同的参数进行测试,而不用为此写多份TEST。类似于TEST_F()宏,我们需要预先定义一个fixture类,而不同之处在于fixture类要继承testing::TestWithParam<T>,而不是testing::Test。TestWithParam也是Test的一个子类,T是任意一个可复制的类,若是一个指针类,那么其所指的对象的生存周期必须由我们自己负责处理。所以TEST_P()功能更多,它具有TEST_F()的所有功能。下面是TEST_P()宏的定义:


#define TEST_P(test_suite_name, test_name)                                    

class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)                    

     : public test_suite_name {                                              

  public:                                                                    

  GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() {}                    

  virtual void TestBody();                                                  

                                                                             

  private:                                                                    

  static int AddToRegistry() {                                              

    ::testing::UnitTest::GetInstance()                                      

         ->parameterized_test_registry()                                      

         .GetTestSuitePatternHolder<test_suite_name>(                        

             #test_suite_name,                                                

            ::testing::internal::CodeLocation(__FILE__, __LINE__))          

         ->AddTestPattern(                                                    

            GTEST_STRINGIFY_(test_suite_name), GTEST_STRINGIFY_(test_name),  

             new ::testing::internal::TestMetaFactory<GTEST_TEST_CLASS_NAME_(

                 test_suite_name, test_name)>());                            

    return 0;                                                                

   }                                                                          

  static int gtest_registering_dummy_ GTEST_ATTRIBUTE_UNUSED_;              

  GTEST_DISALLOW_COPY_AND_ASSIGN_(GTEST_TEST_CLASS_NAME_(test_suite_name,    

                                                          test_name));        

 };                                                                          

int GTEST_TEST_CLASS_NAME_(test_suite_name,                                  

                            test_name)::gtest_registering_dummy_ =            

     GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::AddToRegistry();    

void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody()


该宏展开的结构类似于TEST_F()和TEST()的展开。主要是定义了一个继承fixture类的类,并完成该测试类的注册,TEST_P()的函数体是该类的成员函数TestBody()的函数体。其不同之处在于注册方式有所不同,且TestBody()方法是public的,而不是private。


这里的TestBody的内容是定义获得参数后如何执行测试,这里使用接口函数GetParam()来从参数生成器获取参数。

使用TEST_P()宏时,我们使用INSTANTIATE_TEST_SUITE_P()宏来实例化我们的带参测试,它将定义我们会使用哪些值来做测试。在INSTANTIATE_TEST_SUITE_P()中,我们可以使用Gtest提供的一系列函数(被称为参数生成器)来获取参数。


以下是INSTANTIATE_TEST_SUITE_P宏的定义:


#define INSTANTIATE_TEST_SUITE_P(prefix, test_suite_name, ...)                

static ::testing::internal::ParamGenerator<test_suite_name::ParamType>      

     gtest_##prefix##test_suite_name##_EvalGenerator_() {                    

  return GTEST_EXPAND_(GTEST_GET_FIRST_(__VA_ARGS__, DUMMY_PARAM_));        

 }                                                                          

static ::std::string gtest_##prefix##test_suite_name##_EvalGenerateName_(  

    const ::testing::TestParamInfo<test_suite_name::ParamType>& info) {    

  if (::testing::internal::AlwaysFalse()) {                                

    ::testing::internal::TestNotEmpty(GTEST_EXPAND_(GTEST_GET_SECOND_(      

         __VA_ARGS__,                                                        

         ::testing::internal::DefaultParamName<test_suite_name::ParamType>,  

         DUMMY_PARAM_)));                                                    

    auto t = std::make_tuple(__VA_ARGS__);                                  

    static_assert(std::tuple_size<decltype(t)>::value <= 2,                

                  'Too Many Args!');                                        

   }                                                                        

  return ((GTEST_EXPAND_(GTEST_GET_SECOND_(                                

       __VA_ARGS__,                                                          

       ::testing::internal::DefaultParamName<test_suite_name::ParamType>,    

       DUMMY_PARAM_))))(info);                                              

 }                                                                          

static int gtest_##prefix##test_suite_name##_dummy_                        

     GTEST_ATTRIBUTE_UNUSED_ =                                              

        ::testing::UnitTest::GetInstance()                                  

             ->parameterized_test_registry()                                

             .GetTestSuitePatternHolder<test_suite_name>(                    

                 #test_suite_name,                                          

                ::testing::internal::CodeLocation(__FILE__, __LINE__))      

             ->AddTestSuiteInstantiation(                                    

                 #prefix, &gtest_##prefix##test_suite_name##_EvalGenerator_,

                 &gtest_##prefix##test_suite_name##_EvalGenerateName_,      

                 __FILE__, __LINE__)


该宏的第一个参数是该次实例化的测试名,第二个参数是suite名,第三个参数为参数生成器。该宏的内容较多,大致完成了以下内容:


1.       定义一个获取参数生成器的函数gtest_##prefix##test_suite_name##_EvalGenerator_

2.       定义一个生成参数字符串的函数gtest_##prefix##test_suite_name##_EvalGenerateName_

3.       注册该TestCase的信息。

 

其中parameterized_test_registry注册一个suite的信息


AddTestSuiteInstantiation注册参数生成器的信息。


参考:


————————————————————————————

BY:HW-HH

点赞
收藏
回复
4
分享
举报
浏览2504 编辑于2019-10-24 03:34未知归属地
全部评论
最多点赞
最新发布
最早发布

测试流程很详细了,感谢分享

4楼回复于2019-10-24 07:23 未知归属地

欢迎大家针对GoogleTest常用宏源码进行回帖分享……

3楼回复于2019-10-24 03:36 未知归属地

这只是前篇,还有后篇哦!

2楼回复于2019-10-24 03:35 未知归属地

作者探究的超级详细~

1楼回复于2019-10-24 03:35 未知归属地
写回答
新增插入模板功能
一键使用模板,快速填写内容,轻松发帖~
知道了
  • 为了保障您的信息安全,请勿上传您的敏感个人信息(如您的密码等信息)和您的敏感资产信息(如关键源代码、签名私钥、调试安装包、业务日志等信息),且您需自行承担由此产生的信息泄露等安全风险。
  • 如您发布的内容为转载内容,请注明内容来源。

我要发帖子

了解社区公约,与您携手共创和谐专业的开发者社区。