GTest源码剖析(二)——TEST宏

GTest源码剖析——TEST宏

1 TEST宏定义

简单示例:

TEST(Call,makeCall)
{
    ASSERT_TRUE(true);
}

上述代码展开如下:

//step1:声明一个以test_case_name和test_name拼接成的类。 
//      该类包含一个虚函数TestBody(),主要用于执行编写的测试用例需要执行的逻辑;
//      包含一个静态成员变量test_info_,主要用于注册测试用例信息;
//      申明该类不允许copy和assign操作
class Call_makeCall_Test : public ::testing::Test    
{    
public:    
    Call_makeCall_Test();    

private:    
    //虚函数接口,用于执行用例的函数体
    virtual void TestBody();

    //定义 const static 成员变量。
    static ::testing::TestInfo* const test_info_ ; 

    //该类不许copy和assign操作
    Call_makeCall_Test(Call_makeCall_Test const &);
    void operator=(Call_makeCall_Test const &);
}  

//step2:类静态变量赋初值  
::testing::TestInfo* const Call_makeCall_Test::test_info_ =   
    ::testing::internal::MakeAndRegisterTestInfo(  
        "Call",   
        "makeCall",   
        NULL,   
        NULL,   
        ::testing::internal::CodeLocation(__FILE__, __LINE__), 
        ::testing::internal::GetTestTypeId(),
        ::testing::Test::SetUpTestCase,   
        ::testing::Test::TearDownTestCase,   
        new ::testing::internal::TestFactoryImpl<Call_makeCall_Test>);  

//step3:实例化类成员TestBody() 
//函数体实现为用户编写的需要进行测试的代码逻辑 
void Call_makeCall_Test::TestBody()  
{  
    ASSERT(true);  
}  

2 源码解析

2.1 生成测试用例唯一ID: GetTestTypeId()

其中,GetTestTypeId()的目的是为了生成一个唯一的ID,其实现原理非常简单,取类成员静态变量的地址保证唯一性。
该ID用于唯一的标识一个测试用例。

    //定义TypeId为const void*
    typedef const void* TypeId;

    // 定义了一个只包含静态成员变量的模版类TypeIdHelper
        template <typename T>
        class TypeIdHelper 
    {
        public:
            static bool dummy_;
        };

        template <typename T>
        bool TypeIdHelper<T>::dummy_ = false;

    //获取TypeIdHelper类静态变量的地址
        template <typename T>
        TypeId GetTypeId() 
    {
          return &(TypeIdHelper<T>::dummy_);
        }

    //对GetTypeId()进行封装,主要是为了解决跨平台问题
        TypeId GetTestTypeId() 
    {
          return GetTypeId<Test>();
        }

2.2 记录测试用例位置:CodeLocation

记住该测试用例的文件名称和所在行数,该信息为GTEST新加内容,主要用于后期对测试用例信息的快速定位。

struct CodeLocation 
{
  CodeLocation(const std::string& a_file, int a_line)
      : file(a_file), line(a_line) {}

  std::string file;
  int line;
};

2.3 工厂函数:TestFactoryImpl

实现了一个创建TEST类的工厂函数。
从TestFactoryImpl及其父类TestFactoryBase的实现可以看出,其目的仅仅是实现了一个创建TEST类的工厂函数。

template <class TestClass>
class TestFactoryImpl : public TestFactoryBase 
{
public:
  virtual Test* CreateTest() { return new TestClass; }
};
class TestFactoryBase 
{
public:
  virtual ~TestFactoryBase() {}
  virtual Test* CreateTest() = 0;

protected:
  TestFactoryBase() {}

private:
    TestFactoryBase(TestFactoryBase const &);
    void operator=(TestFactoryBase const &);
};

2.4 注册测试用例信息:MakeAndRegisterTestInfo

介绍了前面的几个接口和类,终于到了MakeAndRegisterTestInfo这个函数。
其目的是对测试用例的信息注册到google test中,为后续允许测试用例进行准备。

MakeAndRegisterTestInfo的代码实现如下:
1. new一个TestInfo类;
2. 通过GetUnitTestImpl接口获取一个UnitTestImpl单例;
3. 把TestInfo添加到UnitTestImpl单例中,即对测试用例的信息进行注册。

GTEST_API_ TestInfo* MakeAndRegisterTestInfo(
    const char* test_case_name, 
    const char* name,
    const char* type_param,             //主要用于TEST_P.TEST中其值为NULL
    const char* value_param,            //主要用于TEST_P.TEST中其值为NULL
    CodeLocation code_location,
    TypeId fixture_class_id,
    SetUpTestCaseFunc set_up_tc,        //主要用于TEST_F
    TearDownTestCaseFunc tear_down_tc,  //主要用于TEST_F
    TestFactoryBase* factory)
{
  //new一个TestInfo实例,保存测试用例的相关信息,后续执行是会添加测试结果。
  TestInfo* const test_info = new TestInfo(
          test_case_name, name, type_param, value_param,code_location, fixture_class_id, factory);

  //添加测试用例信息到UnitTestImpl单例中
  GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info);

  return test_info;
}
2.4.1 UnitTestImpl::AddTestInfo()

再来看一下UnitTestImpl类的AddTestInfo()的实现
1. 该接口直接在类中实现,为inline;
2. 对线程安全的死亡测试进行保护;
3. 根据测试用例信息获取一个TestCase类,如果获取不到则创建一个新的TestCase;
4. 把TestInfo添加到TestCase中。

void AddTestInfo(Test::SetUpTestCaseFunc set_up_tc,
                 Test::TearDownTestCaseFunc tear_down_tc,
                 TestInfo* test_info) 
{

  //为了支持线程安全的DeathTest,记住原始工作目录,这里可以先不关心
  if (original_working_dir_.IsEmpty()) 
  {
    original_working_dir_.Set(FilePath::GetCurrentDir());
    GTEST_CHECK_(!original_working_dir_.IsEmpty()) 
      << "Failed to get the current working directory.";
  }

  GetTestCase(test_info->test_case_name(),
              test_info->type_param(),
              set_up_tc,
              tear_down_tc)->AddTestInfo(test_info);
}
2.4.2 UnitTestImpl::GetTestCase()
  • UnitTestImpl的成员变量test_cases_,用于保存所有的test case.
  • UnitTestImpl的成员变量last_death_test_case_,用于标识死亡测试的数量及索引.
  • UnitTestImpl的成员变量test_case_indices_,用于记录测试用例的索引,便于shuffle and restore
TestCase* UnitTestImpl::GetTestCase(const char* test_case_name,
                                    const char* type_param,
                                    Test::SetUpTestCaseFunc set_up_tc,
                                    Test::TearDownTestCaseFunc tear_down_tc) 
{
  //如果能找到该TestCase则直接返回,否则创建一个新的TestCase
  const std::vector<TestCase*>::const_iterator test_case =
      std::find_if(test_cases_.begin(), test_cases_.end(),TestCaseNameIs(test_case_name));

  if (test_case != test_cases_.end())
    return *test_case;

  TestCase* const new_test_case = 
    new TestCase(test_case_name, type_param, set_up_tc, tear_down_tc);

  //如果该TestCase是死亡测试用例,则插入当前最后一个死亡测试用例的后面,否则插入所有测试用例的后面。
  //死亡测试和普通的测试用例均保存在test_cases_中;
  //用last_death_test_case_标识死亡测试用例的位置;
  //该目的是用去如果运行死亡测试用例,保证其能够最先执行。  
  if (internal::UnitTestOptions::MatchesFilter(test_case_name, kDeathTestCaseFilter)) 
  {
    ++last_death_test_case_;
    test_cases_.insert(test_cases_.begin() + last_death_test_case_, new_test_case);
  } 
  else 
  {
    test_cases_.push_back(new_test_case);
  }

  //记录该TestCase的索引,便于shuffle and restore.
  test_case_indices_.push_back(static_cast<int>(test_case_indices_.size()));
  return new_test_case;
}
2.4.3 TestCase::AddTestInfo

该函数实现比较简单.

  • 把TestInfo放入test_info_list_变量中;
  • 添加test_indices_信息
void TestCase::AddTestInfo(TestInfo * test_info) 
{
  test_info_list_.push_back(test_info);
  test_indices_.push_back(static_cast<int>(test_indices_.size()));
}

3 总结TEST宏

至此,已分析完毕TEST宏的源码。

  1. 首先声明一个以test_case_name和test_name拼接成的类。所以需要保证test_case_name和test_name在代码中唯一,避免命名冲突。
  2. 拼接的类中有一个静态成员变量test_info_,该成员变量的唯一作用是利用类静态变量在main之前初始化,对测试用例的信息进行注册。这是GTEST的最精华部分。
  3. 通过MakeAndRegisterTestInfo函数对test_info_进行赋初值的过程,对测试用例的信息进行注册。因为UnitTest和UnitTestImpl均实现为单例,实现了注册测试用例信息到GTest中。
  4. 通过宏定义,实现了TestBody()的逻辑,即我们需要的测试代码,这部分会在运行测试用例时被调用。
  5. TEST宏的主要目的在于提前注册测试用例信息,为后续运行测试用例做准备。

4 参考

github: googletest


ZhaiPillar
2017-09-16

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值