Google test使用.docx
- 文档编号:30568910
- 上传时间:2023-08-16
- 格式:DOCX
- 页数:19
- 大小:24.54KB
Google test使用.docx
《Google test使用.docx》由会员分享,可在线阅读,更多相关《Google test使用.docx(19页珍藏版)》请在冰豆网上搜索。
Googletest使用
GoogleC++TestingFrameworkPrimer
翻译:
RayLi(ray.leex@)
修改日期:
2008年7月6日
原文参见:
Introduction:
为什么需要GoogleC++测试框架?
GoogleC++测试框架帮助你更好地编写C++测试。
无论你是在Linux,Windows,还是Mac环境下工作,只要你编写C++代码,Google测试框架都可以帮上忙。
那么,哪些因素才能构成一个好的测试?
以及,GoogleC++测试框架怎样满足这些因素?
我们相信:
1.测试应该是独立、可重复的。
因为其他测试成功或失败而导致我们要对自己的测试进行debug是非常痛苦的。
GoogleC++测试框架通过将每个测试在不同的对象中运行,使得测试分离开来。
当一个测试失败时,GoogleC++测试框架允许你独立运行它以进行快速除错。
2.测试应该能够被很好地组织,并反映被测代码的结构。
GoogleC++测试框架将测试组织成测试案例,案例中的测试可以共享数据和程序分支。
这样一种通用模式能够很容易辨识,使得我们的测试容易维护。
当开发人员在项目之间转换,开始在一个新的代码基上开始工作时,这种一致性格外有用。
3.测试应该是可移植、可重用的。
开源社区有很多平台独立的代码,它们的测试也应该是平台独立的。
除开一些特殊情况,GoogleC++测试框架运行在不同的操作系统上、与不同的编译器(gcc、icc、MSVC)搭配,GoogleC++测试框架的测试很容易与不同的配置一起工作。
4.当测试失败时,应该提供尽可能多的、关于问题的信息。
GoogleC++测试框架在第一个测试失败时不会停下来。
相反,它只是将当前测试停止,然后继续接下来的测试。
你也可以设置对一些非致命的错误进行报告,并接着进行当前的测试。
这样,你就可以在一次“运行-编辑-编译”循环中检查到并修复多个bug。
5.测试框架应该能将测试编写人员从一些环境维护的工作中解放出来,使他们能够集中精力于测试的内容。
GoogleC++测试框架自动记录下所有定义好的测试,不需要用户通过列举来指明哪些测试需要运行。
6.测试应该快速。
使用GoogleC++测试框架,你可以重用多个测试的共享资源,一次性完成设置/解除设置,而不用使一个测试去依赖另一测试。
因为GoogleC++测试框架基于著名的xUnit架构,如果你之前使用过JUnit或PyUnit的话,你将会感觉非常熟悉。
如果你没有接触过这些测试框架,它也只会占用你大约10分钟的时间来学习基本概念和上手。
所以,让我们开始吧!
Note:
本文偶尔会用“GoogleTest”来代指“GoogleC++测试框架”。
基本概念
使用GoogleTest时,你是从编写断言开始的,而断言是一些检查条件是否为真的语句。
一个断言的结果可能是成功、非致命失败,或者致命失败。
如果一个致命失败出现,他会结束当前的函数;否则,程序继续正常运行。
测试使用断言来验证被测代码的行为。
如果一个测试崩溃或是出现一个失败的断言,那么,该测试失败;否则该测试成功。
一个测试案例(testcase)包含了一个或多个测试。
你应该将自己的测试分别归类到测试案例中,以反映被测代码的结构。
当测试案例中的多个测试需要共享通用对象和子程序时,你可以把他们放到一个测试固件(testfixture)类中。
一个测试程序可以包含多个测试案例。
从编写单个的断言开始,到创建测试和测试案例,我们将会介绍怎样编写一个测试程序。
断言
GoogleTest中的断言是一些与函数调用相似的宏。
要测试一个类或函数,我们需要对其行为做出断言。
当一个断言失败时,GoogleTest会在屏幕上输出该代码所在的源文件及其所在的位置行号,以及错误信息。
也可以在编写断言时,提供一个自定义的错误信息,这个信息在失败时会被附加在GoogleTest的错误信息之后。
断言常常成对出现,它们都测试同一个类或者函数,但对当前功能有着不同的效果。
ASSERT_*版本的断言失败时会产生致命失败,并结束当前函数。
EXPECT_*版本的断言产生非致命失败,而不会中止当前函数。
通常更推荐使用EXPECT_*断言,因为它们运行一个测试中可以有不止一个的错误被报告出来。
但如果在编写断言如果失败,就没有必要继续往下执行的测试时,你应该使用ASSERT_*断言。
因为失败的ASSERT_*断言会立刻从当前的函数返回,可能会跳过其后的一些的清洁代码,这样也许会导致空间泄漏。
根据泄漏本身的特质,这种情况也许值得修复,也可能不值得我们关心——所以,如果你得到断言错误的同时,还得到了一个堆检查的错误,记住上面我们所说的这一点。
要提供一个自定义的错误消息,只需要使用<<操作符,或一个<<操作符的序列,将其输入到框架定义的宏中。
下面是一个例子:
Cpp代码
1.ASSERT_EQ(x.size(),y.size())<<"Vectorsxandyareofunequallength";
2.for(inti=0;i 3. EXPECT_EQ(x[i],y[i])<<"Vectorsxandydifferatindex"< 4.} ASSERT_EQ(x.size(),y.size())<<"Vectorsxandyareofunequallength"; for(inti=0;i EXPECT_EQ(x[i],y[i])<<"Vectorsxandydifferatindex"< } 任何能够被输出到ostream中的信息都可以被输出到一个断言宏中——特别是C字符串和string对象。 如果一个宽字符串(wchar_t*,windows上UNICODE模式TCHAR*或std: : wstring)被输出到一个断言中,在打印时它会被转换成UTF-8编码。 基本断言 下面这些断言实现了基本的true/false条件测试。 致命断言 非致命断言 验证条件 ASSERT_TRUE(condition); EXPECT_TRUE(condition); condition为真 ASSERT_FALSE(condition); EXPECT_FALSE(condition); condition为假 记住,当它们失败时,ASSERT_*产生一个致命失败并从当前函数返回,而EXCEPT_*产生一个非致命失败,允许函数继续运行。 在两种情况下,一个断言失败都意味着它所包含的测试失败。 有效平台: Linux、Windows、Mac。 二进制比较 本节描述了比较两个值的一些断言。 致命断言 非致命断言 验证条件 ASSERT_EQ(expected,actual); EXPECT_EQ(expected,actual); expected==actual ASSERT_NE(val1,val2); EXPECT_NE(val1,val2); val1! =val2 ASSERT_LT(val1,val2); EXPECT_LT(val1,val2); val1 ASSERT_LE(val1,val2); EXPECT_LE(val1,val2); val1<=val2 ASSERT_GT(val1,val2); EXPECT_GT(val1,val2); val1>val2 ASSERT_GE(val1,val2); EXPECT_GE(val1,val2); val1>=val2 在出现失败事件时,GoogleTest会将两个值(Val1和Val2)都打印出来。 在ASSERT_EQ*和EXCEPT_EQ*断言(以及我们随后介绍类似的断言)中,你应该把你希望测试的表达式放在actual(实际值)的位置上,将其期望值放在expected(期望值)的位置上,因为GoogleTest的测试消息为这种惯例做了一些优化。 参数值必须是可通过断言的比较操作符进行比较的,否则你会得到一个编译错误。 参数值还必须支持<<操作符来将值输入到ostream中。 所有的C++内置类型都支持这一点。 这些断言可以用于用户自定义的型别,但你必须重载相应的比较操作符(如==、<等)。 如果定义有相应的操作符,推荐使用ASSERT_*()宏,因为它们不仅会输出比较的结果,还会输出两个比较对象。 参数表达式总是只被解析一次。 因此,参数表达式有一定的副作用(sideeffect,这里应该是指编译器不同,操作符解析顺序的不确定性)也是可以接受的。 但是,同其他普通C/C++函数一样,参数表达式的解析顺序是不确定的(如,一种编译器可以自由选择一种顺序来进行解析),而你的代码不应该依赖于某种特定的参数解析顺序。 ASSERT_EQ()对指针进行的是指针比较。 即,如果被用在两个C字符串上,它会比较它们是否指向同样的内存地址,而不是它们所指向的字符串是否有相同值。 所以,如果你想对两个C字符串(例如,constchar*)进行值比较,请使用ASSERT_STREQ()宏,该宏会在后面介绍到。 特别需要一提的是,要验证一个C字符串是否为空(NULL),使用ASSERT_STREQ(NULL,c_string)。 但是要比较两个string对象时,你应该使用ASSERT_EQ。 本节中介绍的宏都可以处理窄字符串对象和宽字符串对象(string和wstring)。 有效平台: Linux、Windows、Mac。 字符串比较 该组断言用于比较两个C字符串。 如果你想要比较两个string对象,相应地使用EXPECT_EQ、EXPECT_NE等断言。 致命断言 非致命断言 验证条件 ASSERT_STREQ(expected_str,actual_str); EXPECT_STREQ(expected_str,actual_str); 两个C字符串有相同的内容 ASSERT_STRNE(str1,str2); EXPECT_STRNE(str1,str2); 两个C字符串有不同的内容 ASSERT_STRCASEEQ(expected_str,actual_str); EXPECT_STRCASEEQ(expected_str,actual_str); 两个C字符串有相同的内容,忽略大小写 ASSERT_STRCASENE(str1,str2); EXPECT_STRCASENE(str1,str2); 两个C字符串有不同的内容,忽略大小写 注意断言名称中出现的“CASE”意味着大小写被忽略了。 *STREQ*和*STRNE*也接受宽字符串(wchar_t*)。 如果两个宽字符串比较失败,它们的值会做为UTF-8窄字符串被输出。 一个NULL空指针和一个空字符串会被认为是不一样的。 有效平台: Linux、Windows、Mac。 参见: 更多的字符串比较的技巧(如子字符串、前缀和正则表达式匹配),请参见[AdvancedGuideAdvancedGoogleTestGuide]。 简单的测试 要创建一个测试: 1.使用TEST()宏来定义和命名一个测试函数,它们是一些没有返回值的普通C++函数。 2.在这个函数中,与你想要包含的其它任何有效C++代码一起,使用GoogleTest提供的各种断言来进行检查。 3.测试的结果由其中的断言决定;如果测试中的任意断言失败(无论是致命还是非致命),或者测试崩溃,那么整个测试就失败了。 否则,测试通过。 Cpp代码 1.TEST(test_case_name,test_name){ 2....testbody... 3.} TEST(test_case_name,test_name){ ...testbody... } TEST()的参数是从概括到特殊的。 第一个参数是测试案例的名称,第二个参数是测试案例中的测试的名称。 记住,一个测试案例可以包含任意数量的独立测试。 一个测试的全称包括了包含它的测试案例名称,及其独立的名称。 不同测试案例中的独立测试可以有相同的名称。 举例来说,让我们看一个简单的整数函数: Cpp代码 1.intFactorial(intn);//返回n的阶乘 intFactorial(intn);//返回n的阶乘 这个函数的测试案例应该看起来像是: Cpp代码 1.//测试0的阶乘 2.TEST(FactorialTest,HandlesZeroInput){ 3. EXPECT_EQ(1,Factorial(0)); 4.} 5.//测试正数的阶乘 6.TEST(FactorialTest,HandlesPositiveInput){ 7. EXPECT_EQ(1,Factorial (1)); 8. EXPECT_EQ(2,Factorial (2)); 9. EXPECT_EQ(6,Factorial(3)); 10. EXPECT_EQ(40320,Factorial(8)); 11.} //测试0的阶乘 TEST(FactorialTest,HandlesZeroInput){ EXPECT_EQ(1,Factorial(0)); } //测试正数的阶乘 TEST(FactorialTest,HandlesPositiveInput){ EXPECT_EQ(1,Factorial (1)); EXPECT_EQ(2,Factorial (2)); EXPECT_EQ(6,Factorial(3)); EXPECT_EQ(40320,Factorial(8)); } GoogleTest根据测试案例来分组收集测试结果,因此,逻辑相关的测试应该在同一测试案例中;换句话说,它们的TEST()的第一个参数应该是一样的。 在上面的例子中,我们有两个测试,HandlesZeroInput和HandlesPostiveInput,它们都属于同一个测试案例FactorialTest。 有效平台: Linux、Windows、Mac。 测试固件(TestFixtures,又做测试夹具、测试套件): 在多个测试中使用同样的数据配置 当你发现自己编写了两个或多个测试来操作同样的数据,你可以采用一个测试固件。 它让你可以在多个不同的测试中重用同样的对象配置。 要创建测试固件,只需: 1.创建一个类继承自testing: : Test。 将其中的成员声明为protected: 或是public: ,因为我们想要从子类中存取固件成员。 2.在该类中声明你计划使用的任何对象。 3.如果需要,编写一个默认构造函数或者SetUp()函数来为每个测试准备对象。 常见错误包括将SetUp()拼写为Setup()(小写了u)——不要让它发生在你身上。 4.如果需要,编写一个析构函数或者TearDown()函数来释放你在SetUp()函数中申请的资源。 要知道什么时候应该使用构造函数/析构函数,什么时候又应该使用SetUp()/TearDown()函数,阅读我们的FAQ。 5.如果需要,定义你的测试所需要共享的子程序。 当我们要使用固件时,使用TEST_F()替换掉TEST(),它允许我们存取测试固件中的对象和子程序: Cpp代码 1.TEST_F(test_case_name,test_name){ 2....testbody... 3.} TEST_F(test_case_name,test_name){ ...testbody... } 与TEST()一样,第一个参数是测试案例的名称,但对TEST_F()来说,这个名称必须与测试固件类的名称一些。 你可能已经猜到了: _F正是指固件。 不幸地是,C++宏系统并不允许我们创建一个单独的宏来处理两种类型的测试。 使用错误的宏会导致编译期的错误。 而且,你必须在TEST_F()中使用它之前,定义好这个测试固件类。 否则,你会得到编译器的报错: “virtualoutsideclassdeclaration”。 对于TEST_F()中定义的每个测试,GoogleTest将会: 1.在运行时创建一个全新的测试固件 2.马上通过SetUp()初始化它, 3.运行测试 4.调用TearDown()来进行清理工作 5.删除测试固件。 注意,同一测试案例中,不同的测试拥有不同的测试固件。 GoogleTest在创建下一个测试固件前总是会对现有固件进行删除。 GoogleTest不会对多个测试重用一个测试固件。 测试对测试固件的改动并不会影响到其他测试。 例如,让我们为一个名为Queue的FIFO队列类编写测试,该类的接口如下: Cpp代码 1.template 2.classQueue{ 3.public: 4. Queue(); 5.voidEnqueue(constE&element); 6. E*Dequeue();//返回NULL如果队列为空. 7.size_tsize()const; 8. ... 9.}; template classQueue{ public: Queue(); voidEnqueue(constE&element); E*Dequeue();//返回NULL如果队列为空. size_tsize()const; ... }; 首先,定义一个固件类。 习惯上,你应该把它的名字定义为FooTest,这里的Foo是被测试的类。 Cpp代码 1.classQueueTest: publictesting: : Test{ 2.protected: 3.virtualvoidSetUp(){ 4. q1_.Enqueue (1); 5. q2_.Enqueue (2); 6. q2_.Enqueue(3); 7. } 8.//virtualvoidTearDown(){} 9. Queue 10. Queue 11. Queue 12.}; classQueueTest: publictesting: : Test{ protected: virtualvoidSetUp(){ q1_.Enqueue (1); q2_.Enqueue (2); q2_.Enqueue(3); } //virtualvoidTearDown(){} Queue Queue Queue }; 在这个案例中,我们不需要TearDown(),因为每个测试后除了析构函数外不需要进行其它的清理工作了。 接下来我们使用TEST_F()和这个固件来编写测试。 Cpp代码 1.TEST_F(QueueTest,IsEmptyInitially){ 2. EXPECT_EQ(0,q0_.size()); 3.} 4.TEST_F(QueueTest,DequeueWorks){ 5.int*n=q0_.Dequeue(); 6. EXPECT_EQ(NULL,n); 7. 8. n=q1_.Dequeue(); 9. ASSERT_TRUE(n! =NULL); 10. EXPECT_EQ(1,*n); 11. EXPECT_EQ(0,q1_.size()); 12.deleten; 13. 14. n=q2_.Dequeue(); 15. ASSERT_TRUE(n! =NULL); 16. EXPECT_EQ(2,*n); 17. EXPECT_EQ(1,q2_.size()); 18.deleten; 19.} TEST_F(QueueTest,IsEmptyInitially){ EXPECT_EQ(0,q0_.size()); } TEST_F(QueueTest,DequeueWorks){ int*n=q0_.Dequeue(); EXPECT_EQ(NULL,n); n=q1_.Dequeue(); ASSERT_TRUE(n! =NULL); EXPECT_EQ(1,*n); EXPECT_EQ(0,q1_.size()); deleten; n=q2_.Dequeue(); ASSERT_TRUE(n! =NULL); EXPECT_EQ(2,*n); EXPECT_EQ(1,q2_.size()); deleten; } 上面这段代码既使用了ASSERT_*断言,又使用了EXPECT_*断言。 经验上讲,如果你想要断言失败后,测试能够继续进行以显示更多的错误时,你应该使用EXPECT_*断言;使用ASSERT_*如果该断言失败后继续往下执行毫无意义。 例如,Dequeue测试中的第二个断言是ASSERT_TURE(n! =NULL),因为我们随后会n指针解引用,如果n指针为空的话,会导致一个段错误。 当这些测试开始时,会发生如下情况: 1.GoogleTest创建一个QueueTest对象(我们把它叫做t1)。 2.t1.SetUp()初始化t1。 3.第一个测试(IsEmptyInitiallly)在t1上运行。 4.测试完成后,t1.TearDown()进行一些清理工作。 5.t1被析构。 6.以上步骤在另一个QueueTest对象上重复进行,这回会运行DequeueWorks测试。 有效平台: Linux、Windows、Mac。 注意: 当一个测试对象被构造时,GoogleTest会自动地保存所有的GoogleTest变量标识,对象析构后进行恢复。 调用测试 TEST()和TEST_F()向GoogleTest隐式注册它们的测试。 因此,与很多其他的C++测试框架不同,你不需要为
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Google test使用 test 使用