解读sample1
说明
| 被测试代码文件 | sample1.h、sample1.cc |
| 测试代码文件 | sample1_unittest.cc |
官网上如是描述sample1:
Sample #1 shows the basic steps of using Google Test to test C++ functions.
sample1演示了如何使用gtest来对C++函数进行单元测试。
如何把sample1的代码跑起来,请参考我写的另外一篇文章《用Visual Studio创建集成了gtest的命令行工程》(链接:http://www.cnblogs.com/duxiuxing/p/4272343.html)。
理解被测试代码
被测试代码是两个全局函数:
| 函数名 | 函数功能 |
| Factorial() | 阶乘运算函数 |
| IsPrime() | 判断入参是否为质数 |
理解测试代码:TEST宏
sample1使用了gtest的TEST宏来组织它的测试代码,TEST宏它有两个参数:
- 参数1:test_case_name,对应于被测试的函数,比如“FactorialTest”对应的被测试函数是“Factorial”;
- 参数2:test_name,对应于一组可以独立运行的测试代码。
这似乎跟我以往对单元测试的认知有所出入,我以前的理解是:
- 测试用例(Test Case)是最小的执行单位;
- 一个或几个测试用例组成一个测试包(Test Suite)。
老牌单元测试框架CppUnit里面也是按照Test Suite和Test Case的概念来组织测试代码的。其实,“gtest的TestCase和Test”and“CppUnit的TestSuite和TestCase”,本质上是一回事,它们只是两套框架内对于相同事物的不同称谓而已。实际上不管你习惯于哪套称谓,在阅读文档的时候有疑问的话,结合上下文,应该也是很容易理解的。
CoderZh的大作《玩转Google开源C++单元测试框架Google Test系列(gtest)之一 - 初识gtest》(链接:http://www.cnblogs.com/coderzh/archive/2009/03/31/1426758.html)中提到:
我们使用了TEST这个宏,它有两个参数,官方的对这两个参数的解释为:[TestCaseName,TestName],而我对这两个参数的定义是:[TestSuiteName,TestCaseName]。
在进行单元测试的时候,我们可以用若干组测试代码,从不同的维度来测试同一个函数,test_case_name和test_name是一对多的关系。这种组织关系从sample1的执行结果很容易看出来:

第1组test case的代码是这么组织的:
| 被测试的函数 | Factorial() |
| test case name | FactorialTest |
| - test name 1 | Negative:测试入参<0的情况 |
| - test name 2 | Zero:测试入参=0的情况 |
| - test name 3 | Positive:测试入参>0的情况 |
第2组test case的代码是这么组织的:
| 被测试的函数 | IsPrime() |
| test case name | IsPrimeTest |
| - test name 1 | Negative:测试入参<0的情况 |
| - test name 2 | Trivial:测几个特殊的入参 |
| - test name 3 | Positive:测试入参>0的情况 |
理解测试代码:EXPECT_*断言和ASSERT_*断言
对于sample1的两个被测试函数来说,单元测试要做的事情就是:
- 设定输入;
- 执行被测试函数;
- 判断输出是否符合预期。
在“判断输出是否符合预期”这一步,代码中使用了EXPECT_*宏,我们把这类宏称为断言。当一个EXPECT_*断言检查到不符合预期的情况时(简称为“断言失败”),gtest会在屏幕上输出该断言的位置(源文件路径和代码行号)和错误信息。
与EXPECT_*断言一一对应的还有ASSERT_*断言,它们的区别在于:
- ASSERT_*断言失败时会产生致命失败,并结束当前函数(简单理解就是会调用return)。
- EXPECT_*断言产生非致命失败,代码会继续往下执行,而不会终止当前函数。
两者的使用场景小结:
- 通常更推荐使用EXPECT_*断言,因为在同一个测试函数中,可能存在多处断言失败的情况,使用EXPECT_*断言能够发现一次运行中的所有失败情况。
- 当某个断言失败就没有必要继续往下执行的时候,应该使用ASSERT_*断言。
- ASSERT_*断言失败会立刻从当前的代码返回,这可能导致当前代码之后的一些“清洁回收”代码没有被执行,进而出现内存泄漏。这种问题一旦发现,是应该通过调整测试代码来修复的。
理解测试代码:检查数值比较结果的断言
Factorial()是一个阶乘运算函数,它的原型如下:
// Returns n! (the factorial of n). For negative n, n! is defined to be 1.
int Factorial(int n);
对Factorial()进行单元测试的时候,sample1_unittest.cc中使用了一组检查数值比较结果的断言:
| 致命断言(Fatal assertion) | 非致命断言(Nonfatal assertion) | 预期结果(Verifies) |
| 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 |
理解测试代码:检查布尔值的断言
IsPrime()用于判断入参是否为质数 ,它的原型如下:
// Returns true iff n is a prime number.
bool IsPrime(int n);
对IsPrime()进行单元测试的时候,sample1_unittest.cc使用了一组检查布尔值的断言:
| 致命断言(Fatal assertion) | 非致命断言(Nonfatal assertion) | 预期结果(Verifies) |
| ASSERT_TRUE(condition); | EXPECT_TRUE(condition); | condition is true |
| ASSERT_FALSE(condition); | EXPECT_FALSE(condition); | condition is false |
系列文章索引:http://www.cnblogs.com/duxiuxing/p/4270836.html
解读sample1的更多相关文章
- 解读sample5
说明 被测试代码文件 sample1.h.sample1.cc和sample3-inl.h 测试代码文件 sample5_unittest.cc 官网上如是描述sample5: Sample #5 i ...
- 解读sample3
说明 理解被测试代码 理解测试代码:test fixture简介 编写fixture class TEST_F宏 其他 不应该被忽略的注释 说明 被测试代码文件 sample3-inl.h 测试代码文 ...
- SDWebImage源码解读之SDWebImageDownloaderOperation
第七篇 前言 本篇文章主要讲解下载操作的相关知识,SDWebImageDownloaderOperation的主要任务是把一张图片从服务器下载到内存中.下载数据并不难,如何对下载这一系列的任务进行设计 ...
- SDWebImage源码解读 之 NSData+ImageContentType
第一篇 前言 从今天开始,我将开启一段源码解读的旅途了.在这里先暂时不透露具体解读的源码到底是哪些?因为也可能随着解读的进行会更改计划.但能够肯定的是,这一系列之中肯定会有Swift版本的代码. 说说 ...
- SDWebImage源码解读 之 UIImage+GIF
第二篇 前言 本篇是和GIF相关的一个UIImage的分类.主要提供了三个方法: + (UIImage *)sd_animatedGIFNamed:(NSString *)name ----- 根据名 ...
- SDWebImage源码解读 之 SDWebImageCompat
第三篇 前言 本篇主要解读SDWebImage的配置文件.正如compat的定义,该配置文件主要是兼容Apple的其他设备.也许我们真实的开发平台只有一个,但考虑各个平台的兼容性,对于框架有着很重要的 ...
- SDWebImage源码解读_之SDWebImageDecoder
第四篇 前言 首先,我们要弄明白一个问题? 为什么要对UIImage进行解码呢?难道不能直接使用吗? 其实不解码也是可以使用的,假如说我们通过imageNamed:来加载image,系统默认会在主线程 ...
- SDWebImage源码解读之SDWebImageCache(上)
第五篇 前言 本篇主要讲解图片缓存类的知识,虽然只涉及了图片方面的缓存的设计,但思想同样适用于别的方面的设计.在架构上来说,缓存算是存储设计的一部分.我们把各种不同的存储内容按照功能进行切割后,图片缓 ...
- SDWebImage源码解读之SDWebImageCache(下)
第六篇 前言 我们在SDWebImageCache(上)中了解了这个缓存类大概的功能是什么?那么接下来就要看看这些功能是如何实现的? 再次强调,不管是图片的缓存还是其他各种不同形式的缓存,在原理上都极 ...
随机推荐
- Android群英传》读书笔记 (1) 第一章 Android体系与系统架构 + 第二章 Android开发工具新接触
第一章 Android体系与系统架构 1.Dalvik 和 ARTDalvik好比是一辆可折叠的自行车,平时是折叠的,只有骑的时候,才需要组装起来用.ART好比是一辆组装好了的自行车,装好就可以骑了. ...
- UI实时预览最佳实践(转)
UI实时预览最佳实践 概要:Android中实时预览UI和编写UI的各种技巧.本文的例子都可以在结尾处的示例代码中看到并下载.如果喜欢请star,如果觉得有纰漏请提交issue,如果你有更好的点子可以 ...
- Android Studio学习随笔-移动动画的实现
在上一篇博客我已经讲述了三种事件的实现方法,而现在我用复用方法来实现控件的自动移动,当然要实现控件的移动,先得在activity_main.xml文件中放置一个控件,此处我放置的是一个button控件 ...
- 2015 UESTC Winter Training #10【Northeastern Europe 2009】
2015 UESTC Winter Training #10 Northeastern Europe 2009 最近集训都不在状态啊,嘛,上午一直在练车,比赛时也是刚吃过午饭,状态不好也难免,下次比赛 ...
- 【iOS控制器跳转时,NavigationBar有阴影动画闪过的解决办法】
如题,push控制器时,由于默认的控制器view是黑色,push到这个控制器时,navigationBar(默认是透明效果)后面有一个黑色阴影一闪而过,解决办法将navigationBar设为图片填充 ...
- VI文件编辑操作说明
vi Hello.c (回车后就进入,按i或a键开始编辑.要退出按ESC,进入中间模式,按冒号 :后面跟命令):wq (保存并退出):q!(退出不保存)
- eclise -The method onClick(View) of type new View.OnClickListener(){} must override a superclass method
在做arcgis android开发的时候,突然遇到这种错误,The method onClick(View) of type new View.OnClickListener(){} must ov ...
- iOS程序的完整启动过程(有storyboard)
1.先执行main函数,main内部会调用UIApplicationMain函数 2.UIApplicationMain函数里面做了什么事情:1> 创建UIApplication对象 2> ...
- iOS开发实现登陆
Assumption假设:iOS端加载Web页,然后用户输入用户名密码登陆,WebServer会把用户登陆信息记载在Cookie.那么iOS客户端如何取到Cookie中的登陆信息. 客户端监听 NSH ...
- 重新开始学习javase_类再生(类的合成和继承)
一.合成在新类里简单地创建原有类的对象.我们把这种方法叫作“合成” 为进行合成,我们只需在新类里简单地置入对象句柄即可.举个例子来说,假定需要在一个对象里容纳几个 String对象.两种基本数据类型以 ...