一个例子:
TEST(TestCaseName1, TestName1){
cout << "hello1" << endl;
}
int main(int argc,char* argv[])
{
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
宏的替换本质上是文本的替换,在预处理阶段就已经完成,故我们在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();
}
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::dummy_ variable for each T used to instantiate the template. Therefore, the address of dummy_ is guaranteed to be unique.
return &(TypeIdHelper::dummy_);
}
//调用时传了一个Test,这里的Test是一个范型
TypeId GetTestTypeId()
{
return GetTypeId();
}
通过参数将其测试用例所在的文件名称和所在的行数传递过来,后面调度测试用例的时候可以快速定位
struct CodeLocation {
CodeLocation(const string& a_file, int a_line)
: file(a_file), line(a_line)
{}
string file; //文件名
int line; //行号
};
::testing::Test::SetUpTestCase, //初始化
::testing::Test::TearDownTestCase, //释放
目的是创建了一个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);
};
//创建一个TEST的类
template class TestFactoryImpl : public TestFactoryBase {
public:
virtual Test* CreateTest()
{
return new TestClass;
}
};
前面的几个接口全部包含在这个函数里面,用来做测试用例的准备工作
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;
}
从源码的注释来看,这是为了保护死亡测试的线程安全
如果当前测试用例获取不到一个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);
}
几个变量:
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 ::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;
}
void TestCase::AddTestInfo(TestInfo * test_info) {
test_info_list_.push_back(test_info);
test_indices_.push_back(static_cast<int>(test_indices_.size()));
}
至此,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