//备注:此文章是joven的分享,正巧对手头的工作有帮助,而且借此可以延伸出一个新的需求:若有个用例在不知不觉的情况下,卡死或者阻塞了下面的用例,怎样来对其进行处理?
//这个问题的提出是因为Gtest是同步的,在使用Gtest进行测试时,拉起gtest主线程,通过主线程来控制测试用例的执行,若要解决这个问题,我们就可能需要对Gtest框架进行更改,
//最理想的状态是更改gtest的同步执行为异步执行,在进入每个用例时,由Gtest主线程拉起一个测试用例执行子线程,然后主线程进入wait状态,等待子线程的执行退出,再恢复主线程以进入下一个测试用例的执行,
//如此便可在主线程里进行超时的判断,若超时,则直接kill掉子线程即可。
//虽然想法很美好,但是仍需评估下我们的被测程序的函数实现和一些消息分发处理甚至多线程的处理是否支持在子线程调用,这就是此解决方法最值得思索的地方。
//最近应该会对此解决方法和被测程序进行code review,评估下工作量,论证下可行性,若可行,应该会在12月份动手,初步估计修改框架和调试程序需要大约1周的时间。
在使用gtest做单元测试或接口测试时可能会遇到这样的场景:当某个用例失败时,需要备份当时的执行场景(如系统时间、一些变量值和组件运行日志等),便于测试完成后对失败用例进行重现和分析。
我们首先想到的是在用例中对每个断言的结果进行判断,但如果用例中存在比较多断言或用例很多时,要在每个用例中都增加一堆的if语句,这样很麻烦,代码也不美观。
于是我们研究了下gtest的源码,发现gtest有提供获取当前用例信息的接口:
(1)gtest用例结构
在使用它的接口之前,首先得理解清楚其用例结构。gtest中有3个与用例结构相关的类:TestCase、TestInfo和Test。
TestCase并不是指的用例,而且我们平常说的用例集(test suite),而且Test才是我们平常说的用例,所以TestCase是Test的集合;TestInfo是存放用例信息的类,包括用例集名、用例名和执行结果等信息;TestCase中有个TestInfo*的vector,用于保存用例集中每个用例的信息。
(2)TestInfo和Test的关联
Test类中没有TestInfo的成员,那么它们是怎么关联起来的呢?通过代码调试用例的执行过程发现,在所有用例之前会把用例的信息写进TestInfo*的vector中,而在构造TestInfo对象时,通过一个TestFactoryBase* factory的成员与相应的用例类绑定,执行到某个用例时,再通过相应的CreateTest函数来创建用例对象。
(3)UniTest::GetInstance()
UnitTest是个单件,可以通过UniTest::GetInstance()函数获取单件的实例;
(4) current_test_info()
获取到UnitTest对象后,再通过current_test_info()获取当前用例的信息,它返回的是一个TestInfo对象指针,gtest中一个用例对应一个TestInfo对象;
(5)test_case_name() 和name()
通过TestInfo对象获取到当前的用例名和用例集名,test_case_name()返回用例集名,name()返回用例名称;
(6)result()
用TestInfo的result()函数获取到一个TestResult对象,再调用Passed()函数判断当前用例是否执行成功;
通过上面介绍的接口,可以封装一个GetCurTestInfo的函数,并在用例body后的清理操作TearDown()中调用,当用例失败时就可以做自己想做的事情了,实现代码如下:
1 /** 2 * @brief 获取当前用例执行信息 3 * @param case_name 保存用例集名称 4 * @param name 保存用例名称 5 * @param [IN]len 缓冲区长度 6 * @return bool 用例是否成功 7 */ 8 bool GetCurTestInfo(char* case_name, char* name, const size_t len) 9 { 10 const ::testing::TestInfo* curTest = ::testing::UnitTest::GetInstance()->current_test_info(); 11 const char* cur_case_name = curTest->test_case_name(); 12 const char* cur_name = curTest->name(); 13 strcpy_s(case_name, len, cur_case_name); 14 strcpy_s(name, len, cur_name); 15 const ::testing::TestResult* result = curTest->result(); 16 return result->Passed(); 17 } 18 19 virtual void TearDown() 20 { 21 char casename[200] = {0}; 22 char name[200] = {0}; 23 if(!GetCurTestInfo(casename, name, 200)) 24 { 25 …… // do something 26 } 27 }
最后感谢lewis和domi童鞋一同研究和讨论:)