支持大量对象自动注册

gtest基本使用演示

# main.cpp
int main() {
	testing::InitGoogleTest();
	return RUN_ALL_TESTS();
}

# test_suite_1.cpp
TEST(test_suite_1, case1) {
	EXPECT_EQ(1, 1);
}

TEST(test_suite_1, case2) {
	EXPECT_EQ(1, 1);
}

# test_suite_2.cpp
TEST(test_suite_2, case1) {
	EXPECT_EQ(1, 1);
}

TEST(test_suite_2, case2) {
	EXPECT_EQ(1, 1);
}

问题:这些分散在不同文件中的测试用例,是怎么关联到测试框架的呢?

源码跟踪

来看看TEST宏的定义,其他TEST_F/TEST_P都类似

#define TEST(test_suite_name, test_name) GTEST_TEST(test_suite_name, test_name)

#define GTEST_TEST(test_suite_name, test_name) \
	GTEST_TEST_(test_suite_name, test_name, ::testing::Test, \
				::testing::internal::GetTestTypeId())

#define GTEST_TEST_F(test_suite_name, test_name, parent_class, parent_id) \
	# ...前面有一些检查以及测试类定义,尽量简写 \
	class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)  \
		: public parent_class {                               \
	... };                                                    \
	TestInfo* const GTEST_TEST_CLASS_NAME_(test_suite_name,   \
		 test_name)::test_info_ = MakeAndRegisterTestInfo(...) \ #省略了相关

TEST宏展开后,除了根据test_suite_name和test_name生成一个测试类之外,还紧跟着调用了MakeAndRegisterTestInfo这个全局函数,从函数名我们可以看出来,就是它将测试类给注册到了测试框架中,我们继续跟踪下去

#gtest.cc
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;	
}

函数首先创建了一个TestInfo对象,参数都是来自TEST宏定义的一些属性,然后GetUnitTestImpl()这个函数一眼可以看出,UnitTest肯定单例模式,这是获取UnitTest的单例对象,并调用其addTestInfo方法,事实是这样么,我们继续看下去

#gtest-internal-inl.h

inline UnitTestImpl* GetUnitTestImpl() {
	return UnitTest::GetInstance()->impl();
}

#gtest.cc
UnitTest* UnitTest::GetInstance() {
	static UnitTest instance;
	return &instance;
}

果然UnitTest是一个单例模式,这种单例写法也是现在最主流的模板,基本遇到要写单例直接照抄就行;
好了,前面就是最后一步,UnitTestImpl的addTestInfo方法了,也就是咱们这个test会被存在什么地方

#gtest-internal-inl.h
void UnitTestImpl::AddTestInfo(SetUpTestSuiteFunc set_up_tc,
				 TearDownTestSuiteFunc tear_down_tc,
				 TestInfo* test_info) {
	GetTestSuite(test_info->test_suite_name(), test_info->type_param(),
				 set_up_tc, tear_down_tc)
		->AddTestInfo(test_info);
}

#返回一个TestSuite对象
#UnitTestImpl维护了一个TestSuite数组  std::vector<TestSuite*>
#如果查询的test_suite_name已经存在则返回当前suite,不存在就向数组中添加一个
TestSuite* UnitTestImpl::GetTestSuite(...) 

#gtest.cc
void TestSuite::AddTestInfo(TestInfo* test_info) {
	test_info_list_.push_back(test_info);
	test_idices_.push_back(static_cast<int>(test_indices_.size()));
}

这里代码其实已经很直观了,UnitTestImpl是UnitTest的具体实现,用到的技术点是c++中有名的Impl模式,是一种将实现和接口分离的技术;
UnitTestImpl中维护了一个TestSuite对象的数组,如果我们要添加的这个case的suite_name还不存在,那就新创建一个TestSuite对象,如果存在就直接返回;
而TestSuite才是真正维护case的类,TestSuite中存在一个test_info_list的数组和test_idices数组,前者是所有测试case的集合,后者是用来维护case顺序;

至此,测试用例如何自动注册到测试框架的流程就分析完了;

总结

TEST宏生成了一个Testxxx的测试类,这个类继承了Test基类,同时将其实例化;
UnitTest是一个单例模式,保证了在所有地方都可以通过同一个对象来管理Test类的对象
剩下的MakeAndRegisterTestInfo其实不是必须的,我们也可以直接调用UnitTest类的AddTestInfo,不过这样可能就显得函数比较复杂而已;

这里面用到的技术并不复杂,但是能够给我们提供多对象初始化的思路
例如如下需求:
客户端和服务器是多对一的关系,我们可能存在多种类型的客户端,新增一个客户端类型时,我们的代码除了客户端类的构造外,还涉及到与服务器的添加和删除相关代码,这是很容易遗漏的地方,通过类创建的同时将其自动与服务器关联,则可以解决该问题。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值