注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Koala++'s blog

计算广告学 RTB

 
 
 

日志

 
 

Google Test启蒙篇 [1] (Getting started with Google C++ Testing Framework译文)  

2012-07-26 13:57:22|  分类: C++ |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

Getting started with Google C++ Testing Framework

译者:Koala++ / 屈伟

Introduction: Why Google C++ Testing Framework

Google C++测试框架可以帮助你写出更好的C++测试。

无论你是在LinuxWindows,或是Mac上工作,如果你在使用C++编码,那么Google Test都可以帮助你写测试。

什么是一个优秀的测试呢?Google C++测试框架是如何来帮助我们写出优秀的测试呢?我们相信:

1.     测试应该是独立的可重复的。在依赖于其它测试的结果的环境下调试一个测试是很痛苦的。Google C++测试框架将每个测试都在不同的对象上独立运行,这使得测试之间是独立的。当一个测试失败时,Google C++测试框架允许你在一个独立的环境中去快速调试。

2.     测试应该有着好的组织结构并且反应被测试代码的结构。Google C++测试框架将相关的测试组织成可以共享数据和步骤的测试用例。这种常见的方式使得测试可以有着好的组织结构并使测试容易维护。在程序员开发另一项目开始测试新的测试时,这种测试的一致性特别有用。

3.     测试应该是可移值的可重用的。开源社区有大量的平台无关的代码。Google C++测试也应该是平台无关的。Google C++测试框架可以在不同的操作系统上工作,支持多种编译器(gccMSVC和其它),所以Google C++测试框架可以很容易地支持多种配置。(注意:当前的版本只包含Linux的编译脚本,我们正在积极地编写支持其它平台的脚本。)

4.     当测试失败时,测试框架应该尽可能多地提供关于这个错误的信息Google C++测试框架不会在遇到第一个失败的测试后停止。相反,它只是在这个错误的测试上停止,并且继续执行下一测试。你可以在测试上设置一个错误为非致命的,框架会在遇到这个错误后,这个测试会继续执行。这样,你就可以在一次运行-编辑-编译中检测并解决多个bug

5.     测试框架应该将编写测试的程序员从杂事中解放出来,让他们专注于测试内容Google C++测试框架自动记录所有定义的测试,并且不需要用户通过枚举测试来运行它们。

6.     测试应该是快速的。使用Google C++测试框架,你可以通过一次调用set-up/tear-down就可以让多个测试重用共享资源,并可以使测试之间不相互依赖。

因为Google C++测试框架是基于著名的xUnit架构,所以如果你以前使用过JUnit或是PyUnit,你会感觉用它很熟悉。如果你没有用过,它也只需要花你10分钟的时间来学习,然后你就可以开始使用了。

注意:本文中我们有时将Google Test测试框架非正式地称为Google测试。(在翻译中会使用 GTest)

Setting up a New Test Project

       要使用GTest写测试,你需要先将GTest编译成一个库,然后将你的测试链接上这个库。我们已经为几个常见的编译系统提供了编译文件:在GTest的根目录下,有为Visual Studio提供msvc/xcode/Mac XCodemake/GNU makecodegear/Borland C++ Builder,以及autotools脚本(不建议使用)和为CMakeCMakeLists.txt(建议使用)。如果你的编译系统不在上述系统中,你可以看一下make/Makefile来学习GTest是如何编译的(简单地说你需要将GTEST_ROOT/include加入头文件查找路径,再编译GTEST_ROOTsrc/gtest-all.cc,其中GTEST_ROOTGTest的根目录)。

       你编译了GTest库之后,你可以编译你的测试了。你要确认你已经将GTEST_ROOT加入到头文件查找路径中了,只有这样编译器才能在编译你的测试时找到”gtest/gtest.h”。将你的测试工程链接到GTest库(比如,在Visual Stdio中,可以在gtest.vcproj上添加一个依赖)。

       如果你仍然有疑问,你可以看一下GTest自带的测试,看它们是如何编译和使用的。

Basic Concepts

       你可以通过写断言来开始使用GTest,断言是检查一个条件是否为真的语句。一个断言的结果可以是成功非致命失败致命失败。如果一个致命失败发生了,它会中止当前的函数,否则程序会继续执行。

       测试函数(Tests)用断言来验证被测试代码的行为。如果一个测试崩溃或是有一个失败的断言,那么这个测试函数就失败了,否则测试函数是成功的。

       一个测试用例(Test case)可以包含一个或多个测试函数。你可以将你的测试函数组合到一个测试用例中,这样这些测试函数就可以共享对象和子程序。

一个测试程序(Test program)可以包含多个测试用例。

我们接下来会解释如何写一个测试程序,我们从单个断言开始学起,后来再学习测试语句和测试用例。

Assertions

       GTest断言(s)是由一些看起来像是函数的宏组成的。测试一个类或是函数的方法是通过对它们的行为进行断言。如果断言失败,GTest会打印断言所在的文件名和行号,还有失败信息。你还可以在失败信息之后附加自定义的失败信息。

       断言(s)的名字通常是成对的,一对断言都是测试相应的内容,但对被测试函数有着不同的作用(比如ASSERT_TUREASSERT_FALSE)ASSERT_*断言在失败时产生致命失败,这将会中止当前的函数执行。通常EXPECT_*更好一些,因为它们允许一个测试函数中报告多个失败。但是,如果某些错误是非常严重的错误,一旦发生,没有必要再向下执行时,用ASSERT_*则是合理的。

       因为一个失败的ASSERT_*会立即从当前函数返回,它可能会跳过后面的清理代码,而这可能产生资源泄露。根据泄露的类型,这种泄露可能是需要或不需要修复的。所以你在发现堆内存泄露时不要忘记这可能是断言错误引起的。

       要提供一个自定义的失败信息,只需要用<<操作符流式输出,比如:

ASSERT_EQ(x.size(), y.size()) << "Vectors x and y are of unequal length";

 

for (int i = 0; i < x.size(); ++i) {

  EXPECT_EQ(x[i], y[i]) << "Vectors x and y differ at index " << i;

}

       任何可以流式输出到ostream的类型都可以流式输出到一个断言宏,C字符串和string对象也是可以的。如果是一个宽字符串(Windows中的wchar_t*TCHAR*,或是std::wstring)流式输出到断言,它会在打印时被转换成UTF-8编码。

Basic Assertions

下面的断言用于基本的true/false条件测试。

致命断言

非致命断言

验证

ASSERT_TRUE(condition)

EXPECT_TRUE(condition)

condition为真

ASSERT_FALSE(condition)

EXPECT_FALSE(condition)

condition为假

       切记,当它们失败时,ASSERT_*会产生一个致命失败,并会从当前的函数返回,当EXPECT_*产生一个非致命失败时,允许函数继续执行。但无论哪种失败,它都表示测试中有失败的测试语句。

Binary Comparison

       这节介绍一些比较两个值的断言。

致命断言

非致命断言

验证

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 < val2

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

       在失败的情况下,Google Test会打印val1val2的值。在使用ASSERT_EQ*EXPECT_EQ*(以及随后要介绍的判断相等的断言时),你应该将你要测试的表达式写到actual的位置,将你期望的值写在expected的位置,因为这样做GTest的失败信息会根据这种规则打印。

       你在断言要中所要进行的比较操作,必须是这种类型所支持的,否则你会得到一个编译错误。我们曾在v1.6.0版本之前要求参数支持<<操作符,但现在已经不再强制要求了(如果参数支持<<操作符,它会在断言失败时调用<<操作符打印参数值,如果不支持,GTest就尽它的所能打印参数了。如果想了解更多的自定义打印参数值,可以参见Google Mock Recipe)。

       这些断言支持用户自定义的类型,但你必须支持相应的比较操作符(比如:== <等等)。如果定义了相应的操作符,用ASSERT_*()是一个更好的选择,因为这些断言不仅会打印比较的结果,还会打印两个操作数。

       参数总是只在断言中判断一次。所以参数有副作用是没有问题的。(译注:比如EXPECT_EQ(5, ++i),这里i是有副作用的,它的值会加1)。但是,如像在任何c/c++函数中一样,参数的比较顺序是不定的(编译器可以自由地选择任意顺序),所以你的代码不应该依赖特定的参数比较顺序。(译注:比如:EXPECT_EQ(++i + 5, value + i),这里先执行++i + 5,与先执行value + i的比较结果是不同的)。

       ASSERT_EQ()可以进行指针所指向的值比较。但如果在比较两个c字符串时,它只判断两个指针是否指向同一内存地址,而不是比较内容。所以如果你想比较两个c字符串的值,你应该使用ASSERT_STREQ(),下一节会介绍。如果在判断c字符是否为NULL,可以写ASSERT_EQ(NULL, c_string)。但是,如果比较两个string对象,你应该用ASSERT_EQ

       这节所介绍的宏可以用于比较string或是wstring对象。

String Comparison

       这节介绍比较两个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_STRCASEEQ(str1, str2);

EXPECT_STRCASEEQ(str1, str2);

两个c字符串是否内容不同,忽略大小写

注意:断言名称中的CASE表示忽略大小写。

       *STREQ**STRNE*同样接受宽c字符串参数(wchar_t*),如果比较两个宽字符串失败,它们的值会以UTF-8窄字符串方式打印。

       一个空指针和空字符串在断言判断时是被认为是不相等的。

       更多的字符串比较技巧(子串,前缀,后缀,正式表达式匹配等等),可以在Advanced Google Test Guide中找到。

Simple Tests

       创建一个测试的步骤:

1.     TEST()宏定义一个测试函数名,所定义的这个函数是没有返回值的普通函数。

2.     在这个函数中,你可以写任意合法的c++语句,并用GTest的断言来验证一些变量值。

3.     测试的结果由断言决定,如果测试中的任一断言失败(无论是致命断言或是非致命断言),或是测试崩溃,则整个测试失败。否则,测试成功。

TEST(test_case_name, test_name) {

 ... test body ...

}    

TEST()中的第一个参数是测试用例的名字,第二个参数是测试函数的名字。两个名字中都必须是合法的C++函数名字符,并且不应该包含下划线(_)。一个测试函数的完整名称包括测试用例的名称和它自身名称。不同测试用例中的测试函数可以有相同的名称。

       下面以一个简单的函数为例:

int Factorial(int n); //返回n的阶乘

       对这个函数的测试用例可能是这样的:

// Tests factorial of 0.

TEST(FactorialTest, HandlesZeroInput) {

  EXPECT_EQ(1, Factorial(0));

}

 

// Tests factorial of positive numbers.

TEST(FactorialTest, HandlesPositiveInput) {

  EXPECT_EQ(1, Factorial(1));

  EXPECT_EQ(2, Factorial(2));

  EXPECT_EQ(6, Factorial(3));

  EXPECT_EQ(40320, Factorial(8));

}

       Google Test以测试用例的方式将测试函数组织起来,所以逻辑上相关的测试函数应该属于同一测试用例,换言之,这些测试函数的TEST()中的第一个参数应该是相同的。在前面的例子中,我们有两个测试函数,HandleZeroInputHandlePositiveInput,它们属于同一测试用例FactorialTest

  评论这张
 
阅读(2715)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017