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