1.1 gtest注册&调用主体流程
我们在gtest框架里每写一个TEST(suitName,caseName)函数,就相当于定义了一个独一无二的Test类,这个Test类的类名由suitName与caseName拼接的来,而我们在TEST函数里写的测试代码,就是这个类的一个名叫TestBody的虚函数实现,具体的实现过程分析如下
- Test用例注册
- TEST宏
在gtest/gtest.h 文件中找到TEST的宏的定义
我们可以看到GTEST_TEST/TEST/TEST_F 宏的事件机制都调用的GTEST_TEST_ 宏,然后继续追踪这个宏的定义#define GTEST_TEST(test_suite_name, test_name) \ GTEST_TEST_(test_suite_name, test_name, ::testing::Test, \ ::testing::internal::GetTestTypeId()) #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>())
#define GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \ test_case_name##_##test_name##_Test_ #define GTEST_TEST_(test_suite_name, test_name, parent_class, parent_id) \ static_assert(sizeof(GTEST_STRINGIFY_(test_suite_name)) > 1, \ "test_suite_name must not be empty"); \ static_assert(sizeof(GTEST_STRINGIFY_(test_name)) > 1, \ "test_name must not be empty"); \ 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()
- TEST宏
将TEST宏完全展开后,我们得到了一个类名由suitName与caseName拼接而来的Test类,我们先不关注其他成员的定义,主要关注这个类下的TestBody虚函数,神奇的地方在于,我们在这个宏定义后接上花括号与测试代码,就完成了对TestBody函数的实现,并且不会出现语法上的错误
贴上最简单的TEST函数示意,用于对照
TEST(suit1,case1) {
EXPECT_EQ(1+1,2)
}
// 此时我们相当于定义了一个名为suit1_case1_的Test类,并且实现了类中的TestBody方法如下
// ........省略该方法上面的宏
// void suit1_case1_::TestBody() {
// EXPECCT_EQ(1+1,2)
// }
我们已经了解到TEST宏其实是定义了一个类,那么框架是如何在我们没有主动调用TestBody方法的前提下运行我们已经写好的测试代码呢?这就涉及到注册流程,注意到TEST宏展开后的这段代码如下,每个我们定义的Test类里都调用了MakeAndRegisterTestInfo函数,这便是注册函数,习惯使用C语言的同学可能会有疑问:为什么静态全局变量能在初始化( main函数之前)之前动态赋值? 但这在g++编译时的确是被允许的,有兴趣的同学可以自行试验。
::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)>); \
- MakeAndRegisterTestInfo函数
可以看到MakeAndRegisterTestInfo函数中调用了AddTestInfo函数,这个函数的主要功能就是将test_info加入全局的test_info_list数组中,而test_info中包含我们所定义类的工厂类,这样在框架就能遍历test_info_list,构造我们定义的Test类,并且调用其中的TestBody方法!
TestInfo* MakeAndRegisterTestInfo(
const char* test_suite_name, const char* name, const char* type_param,
const char* value_param, CodeLocation code_location,
TypeId fixture_class_id, SetUpTestSuiteFunc set_up_tc,
TearDownTestSuiteFunc tear_down_tc, TestFactoryBase* factory) {
TestInfo* const test_info =
new TestInfo(test_suite_name, name, type_param, value_param,
code_location, fixture_class_id, factory);
GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info);
return test_info;
}
每个test_info中都有一个对应的TestFactoryImp用于生成对应的Test类,于是框架便可调用我们在每个Test类中自定义的TestBody方法,贴上TestFactoryImp的实现,供有兴趣的同学自行品味
// This class provides implementation of TeastFactoryBase interface.
// It is used in TEST and TEST_F macros.
template <class TestClass>
class TestFactoryImpl : public TestFactoryBase {
public:
Test* CreateTest() override { return new TestClass; }
};
- RUN_ALL_TESTS宏
注册流程结束后,接下来是真正的调用流程,gtest框架使用时,会要求我们在main函数内调用RUN_ALL_TEST宏,,在gtest.h中可以找到该宏的定义
inline int RUN_ALL_TESTS() {
return ::testing::UnitTest::GetInstance()->Run();
}
RUN_ALL_TESTS就是简单的调用UnitTest的成员函数GetInstance,返回一个单例(Singleton)UnitTest对象,该对象调用成员函数Run,在gtest.cc 文件中找到 UntiTest::Run 的核心代码
int UnitTest::Run(){
impl()->set_catch_exceptions(GTEST_FLAG(catch_exceptions));
return internal::HandleExceptionsInMethodIfSupported(
impl(),
&internal::UnitTestImpl::RunAllTests,
"auxiliary test code (environments or event listeners)") ? 0 : 1;}
可以看到Run 函数调用了HandleExceptionsInMethodIfSupported函数,该函数的第一个参数为模板类,第二个参数为该模板类内的一个无参方法,逻辑为执行该模板类内指定的方法,并捕获其中的异常
template <class T, typename Result>
Result HandleExceptionsInMethodIfSupported(
T* object, Result (T::*method)(), const char* location) {
if (internal::GetUnitTestImpl()->catch_exceptions()) {
...... //异常处理省略
} else {
return (object->*method)();
}
}
internal::UnitTestImpl::RunAllTests,核心代码如下
if (!Test::HasFatalFailure()) {
for (int test_index = 0; test_index < total_test_suite_count();
test_index++) {
GetMutableSuiteCase(test_index)->Run();
}
}
这段代码功能为遍历我们所定义的testsuit,并调用其中的Run方法,其实就是在调用我们TEST_F中测试套中的Setup方法。
TestInfo* TestSuite::GetMutableTestInfo(int i) {
const int index = GetElementOr(test_indices_, i, -1);
return index < 0 ? nullptr : test_info_list_[static_cast<size_t>(index)];
}
接下来遍历test_info,test_info_list就是记录我们通过TEST宏注册的test_info数组,test_info记录了每个TEST用例的类信息,还包含了构造该类的工厂类,继续追踪TestInfo 的Run方法,可以看到核心代码如下,调用了工厂类的CreateTest方法, 构造了我们定义好的Test类并调用了该类的Run方法。
void TestInfo::Run() {
Test* const test = internal::HandleExceptionsInMethodIfSupported(
factory_, &internal::TestFactoryBase::CreateTest,
"the test fixture's constructor");
if (!Test::HasFatalFailure() && !Test::IsSkipped()) {
// This doesn't throw as all user code that can throw are wrapped into
// exception handling code.
test->Run(); // Run方法最终还是通过handleExceptions方法调用了TestBody
}
//终于调到了这里,非常的关键
void Test::Run() {
if (!HasFatalFailure() && !IsSkipped()) {
impl->os_stack_trace_getter()->UponLeavingGTest();
internal::HandleExceptionsInMethodIfSupported(
this, &Test::TestBody, "the test body");
}
最终Test:Run调用了HandleExceptionsInMethodIfSupported函数,第一个参数为该Test类,第二个参数为我们定义的TestBody方法,再来看看HandleExceptionsInMethodIfSupported函数的实现,我们定义的TestBody方法正是在这里被调用的!至此整个gtest的注册、调用流程就已经呈现完毕
template <class T, typename Result>
Result HandleExceptionsInMethodIfSupported(
T* object, Result (T::*method)(), const char* location) {
if (internal::GetUnitTestImpl()->catch_exceptions()) {
...... //异常处理省略
} else {
return (object->*method)(); //这里相当于执行了 Test:TestBody()方法
}
}
```
***