Google Test源码浅析(二) ——– TEST宏
一、TEST宏的定义
一个例子:
TEST(TestCaseName1, TestName1){
cout << "hello1" << endl;
}
int main(int argc,char* argv[])
{
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
123456789
宏的替换本质上是文本的替换,在预处理阶段就已经完成,故我们在linux下可以看到预处理阶段所完成的事情
//表明这是一个以test_case_name和test_name拼接起来的类
//同时继承继而父类Test
//一个执行编写测试用例的逻辑
//一个静态成员test_info_,主要用于注册测试用例信息
//不能拷贝和赋值
class TestCaseName1_TestName1_Test : public ::testing::Test
{
public:
TestCaseName1_TestName1_Test()
{}
private:
virtual void TestBody();
static ::testing::TestInfo* const test_info_ __attribute__ ((unused));
TestCaseName1_TestName1_Test(TestCaseName1_TestName1_Test const &);
void operator=(TestCaseName1_TestName1_Test const &);
};
//静态成员的初始化
::testing::TestInfo* const TestCaseName1_TestName1_Test ::test_info_ =
::testing::internal::MakeAndRegisterTestInfo(//注册信息后面解释
"TestCaseName1", "TestName1", __null, __null,
::testing::internal::CodeLocation("test1.cc", 6),//快速定位(::testing::internal::GetTestTypeId()), //生成唯一id,后面解释
::testing::Test::SetUpTestCase, //初始化
::testing::Test::TearDownTestCase, //释放
new ::testing::internal::TestFactoryImpl< TestCaseName1_TestName1_Test>); //工厂函数,后面解释
//执行的函数体
void TestCaseName1_TestName1_Test::TestBody(){
cout << "hello1" << endl;
}
int main(int argc,char* argv[])
{
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
123456789101112131415161718192021222324252627282930313233343536
二、源码
1. 生成唯一测试用例ID——— GetTestTypeId
GetTestTypeId()的目的是为了生成一个唯一的ID,其实现原理非常简单,取类成员静态变量的地址保证唯一性。 该ID用于唯一的标识一个测试用例。
//TypeId是一个指针
typedef const void* TypeId;
template <typename T>
class TypeIdHelper {
public:
//是一个静态成员变量,其初值为false,当生成测试用例ID时,就变为true,将此地址作为测试用例的唯一id
static bool dummy_;
};
template <typename T>
TypeId GetTypeId() { //分配出不同的静态成员变量,这个地址是唯一的
// The compiler is required to allocate a different
// TypeIdHelper<T>::dummy_ variable for each T used to instantiate the template. Therefore, the address of dummy_ is guaranteed to be unique.
return &(TypeIdHelper<T>::dummy_);
}
//调用时传了一个Test,这里的Test是一个范型
TypeId GetTestTypeId()
{
return GetTypeId<Test>();
}
1234567891011121314151617181920
2. 记录测试用例的位置—— CodeLocation
通过参数将其测试用例所在的文件名称和所在的行数传递过来,后面调度测试用例的时候可以快速定位
struct CodeLocation {
CodeLocation(const string& a_file, int a_line)
: file(a_file), line(a_line)
{}
string file; //文件名
int line; //行号
};
12345678
3. 建立测试用例和释放测试用例
::testing::Test::SetUpTestCase, //初始化
::testing::Test::TearDownTestCase, //释放
12
4. 工厂函数———– TestFactoryImpl()
目的是创建了一个TEST类
//只仅仅提供了一个基类
class TestFactoryBase {
public:
virtual ~TestFactoryBase() {}
// Creates a test instance to run. The instance is both created and destroyed
// within TestInfoImpl::Run()
virtual Test* CreateTest() = 0; //纯虚函数
protected:
TestFactoryBase() {}
private:
GTEST_DISALLOW_COPY_AND_ASSIGN_(TestFactoryBase);
};
1234567891011121314
//创建一个TEST的类
template <class TestClass> class TestFactoryImpl : public TestFactoryBase {
public:
virtual Test* CreateTest()
{
return new TestClass;
}
};
12345678
5. 注册测试用例信息———- MakeAndRegisterTestInfo
前面的几个接口全部包含在这个函数里面,用来做测试用例的准备工作
MakeAndRegisterTestInfo的代码逻辑:
new 一个TestInfo类
通过GetUnitTestImpl接口获取一个UnitTestImpl单例
把TestInfo添加到UnitTestImpl单例中,即对测试用例的信息进行注册
源码:
TestInfo* MakeAndRegisterTestInfo(//创建一个类并且注册TestInfo
const char* test_case_name, //TEST宏的两个参数
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一个test_info实例,保存测试用例相关的信息,后续执行时添加测试结果
TestInfo* const test_info =
new TestInfo(test_case_name, name, type_param, value_param,code_location, fixture_class_id, factory);
GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info);//将实例添加到UnitTestImpl单例中
return test_info;
}
12345678910111213141516
6. UnitTestImpl::AddTestInfo()
从源码的注释来看,这是为了保护死亡测试的线程安全
如果当前测试用例获取不到一个Testcase类,就创建一个TestCase类
将test_info的信息添加到TestCase类中
void AddTestInfo(Test::SetUpTestCaseFunc set_up_tc,
Test::TearDownTestCaseFunc tear_down_tc,
TestInfo* test_info) {
// In order to support thread-safe death tests, we need to
// remember the original working directory when the test program
// was first invoked. We cannot do this in RUN_ALL_TESTS(), as
// the user may have changed the current directory before calling
// RUN_ALL_TESTS(). Therefore we capture the current directory in
// AddTestInfo(), which is called to register a TEST or TEST_F
// before main() is reached.
//这是为了支持线程安全的死亡测试所做的工作,将原始的工作目录记录下来
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);
}
12345678910111213141516171819202122
7. UnitTestImpl::GetTestCase()
几个变量:
test_cases_是一个vector,保存着所有的测试用例last_death_test_case_是vector里死亡测试数量,也是死亡测试用例的下标test_case_indices_是记录当前测试用例的数量
TestCase* UnitTestImpl::GetTestCase(const char* test_case_name,
const char* type_param,
Test::SetUpTestCaseFunc set_up_tc,
Test::TearDownTestCaseFunc tear_down_tc) {
// Can we find a TestCase with the given name? 将所有的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;
// No. Let's create one. 如果没有就创建一个
TestCase* const new_test_case =
new TestCase(test_case_name, type_param, set_up_tc, tear_down_tc);
// Is this a death test case? 判断当前是否为死亡测试
if (internal::UnitTestOptions::MatchesFilter(test_case_name,
kDeathTestCaseFilter)) {
//如果是死亡测试,就将其插入到最后一个死亡测试用例的后面,不是就将其插入到所有测试用例的后面
//当还没有被洗牌,使死亡测试最先指向,否则会先执行非死亡测试,将死亡测试放到最后在执行
// Yes. Inserts the test case after the last death test case
// defined so far. This only works when the test cases haven't
// been shuffled. Otherwise we may end up running a death test after a non-death test.
++last_death_test_case_;
test_cases_.insert(test_cases_.begin() + last_death_test_case_,
new_test_case);
} else {
// No. Appends to the end of the list.
test_cases_.push_back(new_test_case);
}
//最后保存该测试用例的节点号,为了重新洗牌和恢复
test_case_indices_.push_back(static_cast<int>(test_case_indices_.size()));
return new_test_case;
}
123456789101112131415161718192021222324252627282930313233343536
8. 最后来看一下GetCase中的AddTestInfo
目的是保存测试特例,因为每一个测试用例都包含多组测试特例将其索引也保存下来,方便后面调度的时候好找
void TestCase::AddTestInfo(TestInfo * test_info) {
test_info_list_.push_back(test_info);
test_indices_.push_back(static_cast<int>(test_indices_.size()));
}
1234
三、总结
至此,TEST宏的源码已经全部展示完毕;我们可以看出TEST宏将运行测试用例之前的工作全部做完,接下来就是运行测试用例的过程;这些准备工作有以下几个步骤:
将test_case_name和test_name拼接起来,实现静态的多态行为
使用静态成员test_info_进行初始化测试用例的各种信息,对测试用例的所有信息进行注册
在注册信息时使用MakeAndRegisterTestInfo对test_info_进行初始化和填充信息
通过TestBody()实现对测试特例代码的执行,在后面执行代码时被调用
在注册信息中用到了 UnitTest和UnitTestImpl,二者均为单例,实现了注册测试用例信息到GTest中。
注:以上参考https://blog.csdn.net/pillary/article/details/78014546#24-%E6%B3%A8%E5%86%8C%E6%B5%8B%E8%AF%95%E7%94%A8%E4%BE%8B%E4%BF%A1%E6%81%AFmakeandregistertestinfo