http://www.uml.org.cn/c++/201203293.asp

googletest是一个用来写C++单元测试的框架,它是跨平台的,可应用在windows、linux、Mac等OS平台上。下面,我来说明如何使用最新的1.6版本gtest写自己的单元测试。

本文包括以下几部分:1、获取并编译googletest(以下简称为gtest);2、如何编写单元测试用例;3、如何执行单元测试。4、google test内部是如何执行我们的单元测试用例的。

1. 获取并编译gtest

gtest试图跨平台,理论上,它就应该提供多个版本的binary包。但事实上,gtest只提供源码和相应平台的编译方式,这是为什么呢?google的解释是,我们在编译出gtest时,有些独特的工程很可能希望在编译时加许多flag,把编译的过程下放给用户,可以让用户更灵活的处理。这个仁者见仁吧,反正也是免费的BSD权限。

源码的获取地址:http://code.google.com/p/googletest/downloads/list

目前gtest提供的是1.6.0版本,我们看看与以往版本1.5.0的区别:

Changes for 1.6.0:

* New feature: ADD_FAILURE_AT() for reporting a test failure at the

given source location -- useful for writing testing utilities.

。。。 。。。

* Bug fixes and implementation clean-ups.

* Potentially incompatible changes: disables the harmful 'make install'

command in autotools.

就是最下面一行,make install禁用了,郁闷了吧?UNIX的习惯编译方法:./configure;make;make install失灵了,只能说google比较有种,又开始挑战用户习惯了。

那么怎么编译呢?

先进入gtest目录(解压gtest.zip包过程就不说了),执行以下两行命令:

g++ -I./include -I./ -c ./src/gtest-all.cc

ar -rv libgtest.a gtest-all.o

之后,生成了libgtest.a,这个就是我们要的东东了。以后写自己的单元测试,就需要libgtest.a和gtest目录下的include目录,所以,这1文件1目录我们需要拷贝到自己的工程中。

编译完成后怎么验证是否成功了呢?(相当不友好!)

cd ${GTEST_DIR}/make

make

./sample1_unittest

如果看到:

Running main() from gtest_main.cc

[==========] Running 6 tests from 2 test cases.

[----------] Global test environment set-up.

[----------] 3 tests from FactorialTest

[ RUN ] FactorialTest.Negative

[ OK ] FactorialTest.Negative (0 ms)

[ RUN ] FactorialTest.Zero

[ OK ] FactorialTest.Zero (0 ms)

[ RUN ] FactorialTest.Positive

[ OK ] FactorialTest.Positive (0 ms)

[----------] 3 tests from FactorialTest (0 ms total)

[----------] 3 tests from IsPrimeTest

[ RUN ] IsPrimeTest.Negative

[ OK ] IsPrimeTest.Negative (0 ms)

[ RUN ] IsPrimeTest.Trivial

[ OK ] IsPrimeTest.Trivial (0 ms)

[ RUN ] IsPrimeTest.Positive

[ OK ] IsPrimeTest.Positive (0 ms)

[----------] 3 tests from IsPrimeTest (0 ms total)

[----------] Global test environment tear-down

[==========] 6 tests from 2 test cases ran. (0 ms total)

[ PASSED ] 6 tests.

那么证明编译成功了。

2、如何编写单元测试用例

以一个例子来说。我写了一个开地址的哈希表,它有del/get/add三个主要方法需要测试。在测试的时候,很自然,我只希望构造一个哈希表对象,对之做许多种不同组合的操作,以验证三个方法是否正常。所以,gtest提供的TEST方式我不会用,因为多个TEST不能共享同一份数据,而且还有初始化哈希表对象的过程呢。所以我用TEST_F方式。TEST_F是一个宏,TEST_F(classname, casename){}在函数体内去做具体的验证。

上面是我要执行单元测试的类图。那么,我需要写一系列单元测试用例来测试这个类。用gtest,首先要声明一个类,继承自gtest里的Test类:

代码很简单:

class CHashTableTest : public ::testing::Test {

protected:

CHashTableTest():ht(100){

}

virtual void SetUp() {

key1 = "testkey1";

key2 = "testkey2";

}

// virtual void TearDown() {}

CHashTable ht;

string key1;

string key2;

};

然后开始写测试用例,用例里可以直接使用上面类中的成员。

TEST_F(CHashTableTest, hashfunc)

{

CHashElement he;

ASSERT_NE(\

ht.getHashKey((char*)key1.c_str(), key1.size(), 0),\

ht.getHashKey((char*)key2.c_str(), key2.size(), 0));

ASSERT_NE(\

ht.getHashKey((char*)key1.c_str(), key1.size(), 0),\

ht.getHashKey((char*)key1.c_str(), key1.size(), 1));

ASSERT_EQ(\

ht.getHashKey((char*)key1.c_str(), key1.size(), 0),\

ht.getHashKey((char*)key1.c_str(), key1.size(), 0));

}

注意,TEST_F宏会直接生成一个类,这个类继承自上面我们写的CHashTableTest类。

gtest提供ASSERT_和EXPECT_系列的宏,用于判断二进制、字符串等对象是否相等、真假等等。这两种宏的区别是,ASSERT_失败了不会往下执行,而EXPECT_会继续。

3、如何执行单元测试

首先,我们自己要有一个main函数,函数内容非常简单:

#include "gtest/gtest.h"

int main(int argc, char** argv) {

testing::InitGoogleTest(&argc, argv);

// Runs all tests using Google Test.

return RUN_ALL_TESTS();

}

InitGoogleTest会解析参数。RUN_ALL_TESTS会把整个工程里的TEST和TEST_F这些函数全部作为测试用例执行一遍。

执行时,假设我们编译出的可执行文件叫unittest,那么直接执行./unittest就会输出结果到屏幕,例如:

[==========] Running 4 tests from 1 test case.

[----------] Global test environment set-up.

[----------] 4 tests from CHashTableTest

[ RUN ] CHashTableTest.hashfunc

[ OK ] CHashTableTest.hashfunc (0 ms)

[ RUN ] CHashTableTest.addget

[ OK ] CHashTableTest.addget (0 ms)

[ RUN ] CHashTableTest.add2get

testCHashTable.cpp:79: Failure

Value of: getHe->m_pNext==NULL

Actual: true

Expected: false

[ FAILED ] CHashTableTest.add2get (1 ms)

[ RUN ] CHashTableTest.delget

[ OK ] CHashTableTest.delget (0 ms)

[----------] 4 tests from CHashTableTest (1 ms total)

[----------] Global test environment tear-down

[==========] 4 tests from 1 test case ran. (1 ms total)

[ PASSED ] 3 tests.

[ FAILED ] 1 test, listed below:

[ FAILED ] CHashTableTest.add2get

</pre><p>可以看到,对于错误的CASE,会标出所在文件及其行数。</p><p></p>如果我们需要输出到XML文件,则执行./unittest --gtest_output=xml,那么会在当前目录下生成test_detail.xml 文件,内容如下:<p></p><p></p><pre name="code" class="cpp"><?xml version="1.0" encoding="UTF-8"?>

<testsuites tests="3" failures="0" disabled="0" errors="0" time="0.001" name="AllTests">

<testsuite name="CHashTableTest" tests="3" failures="0" disabled="0" errors="0" time="0.001">

<testcase name="hashfunc" status="run" time="0.001" classname="CHashTableTest" />

<testcase name="addget" status="run" time="0" classname="CHashTableTest" />

<testcase name="delget" status="run" time="0" classname="CHashTableTest" />

</testsuite>

</testsuites>

如此,一个简单的单元测试写完。因为太简单,所以不需要使用google mock模拟一些依赖。后续我再写结合google mock来写一些复杂的gtest单元测试。

下面来简单说下gtest的工作流程。

4、google test内部是如何执行我们的单元测试用例的

首先从main函数看起。

我们的main函数执行了RUN_ALL_TESTS宏,这个宏干了些什么事呢?

#define RUN_ALL_TESTS()\

(::testing::UnitTest::GetInstance()->Run())

} // namespace testing

原来是调用了UnitTest静态工厂实例的Run方法!在gtest里,一切测试用例都是Test类的实例!所以,Run方法将会执行所有的Test实例来运行所有的单元测试,看看类图:

原来是调用了UnitTest静态工厂实例的Run方法!在gtest里,一切测试用例都是Test类的实例!所以,Run方法将会执行所有的Test实例来运行所有的单元测试,看看类图:

以上并没有深入细节,只是大致帮助大家理解,我们写的几个简单的gtest宏,和单元测试用例,到底是如何被执行的。接下来,我会通过gmock来深入的看看google单元测试的玩法。

如何用googletest写单元测试的更多相关文章

  1. 如何用ActiveQt写导出类

    如何用ActiveQt写导出类 最近一直在用ActiveQt框架来写ActiveX插件, 由于项目需要提示类的导出, 所以上午捣鼓了一下, 现在记录记录.其实内容主要是把Qt手册里自己用到的部分整理一 ...

  2. 单元测试er——为什么真的真的要写单元测试

    优点 为什么很多技术或者知识要说优点?因为有些道理看着很简单,大家表面上都觉得对,但是做的时候又不去做或者做不到.其中有一个很重要原因是骨子里或者潜意识并没有真实觉得这是对的,一旦想去做的时候同时会冒 ...

  3. 为什么从前那些.NET开发者都不写单元测试呢?

    楔子 四年前我虽然也写了很多年代码,由于公司虽然规模不小,却并非一家规范化的软件公司,因此在项目中严格意义上来说并没有架构设计.也不写单元测试,后来有幸加入了一家公司,这家公司虽然也是一家小公司,但是 ...

  4. VSTS写单元测试

                          用VSTS写单元测试                           许多应用程序都会用到“用户”类型,今天我要用的是ConsoleApplicatio ...

  5. 如何为 Vue 项目写单元测试

    https://www.w3ctech.com/topic/2052 如何为 Vue 项目写单元测试 前端工程 明非 2017-07-18 4685 访问 1 分享 微信分享 译者:明非 链接:htt ...

  6. 为什么不针对internal接口写单元测试?

    测试驱动的开发(TDD,Test Driven Development)的核心理念,是要使得重构(refactoring)更为有效,而不是创建更多的测试. 对一个有着长生命周期的项目来讲,在它的第一个 ...

  7. linux常用终端指令+如何用vim写一个c程序并运行

    在装好ubuntu之后今天学习了一些linux的一些基础知识: windows里面打开命令窗口是win+r,在linux系统里面,ctrl+alt+t打开终端,今天的一些指令都是围绕终端来说的 首先s ...

  8. 2019-2-13-Latex-论文elsevier,手把手如何用Latex写论文

    title author date CreateTime categories Latex 论文elsevier,手把手如何用Latex写论文 lindexi 2019-02-13 10:38:20 ...

  9. 【快学springboot】在springboot中写单元测试[Happyjava]

    前言 很多公司都有写单元测试的硬性要求,在提交代码的时候,如果单测通不过或者说单元测试各种覆盖率不达标,会被拒绝合并代码.写单元测试,也是保证代码质量的一种方式. junit单元测试 相信绝大多数的J ...

随机推荐

  1. awk的日志模块追加日期时间字段的方案

    function test() { i = ) { system("sleep 1") "date +'%Y-%m-%d %H:%M:%S'" | getlin ...

  2. VMware Network Adapter VMnet1和VMnet8 未识别的网络的解决方法

    VMware Network Adapter VMnet1和VMnet8 被防火墙认定为未识别的网络,阻隔,无法使用端口映射,虚拟机的80端口无法传入,数据包只能出不能入.且公用网络被限制不能修改为家 ...

  3. zabbix报警把特定的应用集发送给developer

    1.创建新的action 2.创建新的develop用户 3.需要删除组不然会给组里所有人发

  4. svn由于连接方在一段时间后没有正确答复或连接的主机没有反应连接尝试失败

    解决方法,关掉防火墙, service iptables status 查看iptables状态 service iptables restart iptables服务重启 service iptab ...

  5. Jtree(节点的渲染+资源管理器)(2)

    上一次我们建立一个比较简单的资源管理器,这次我们说一下上面的资源管理器的问题,并且得尽量的贴近windows的资源管理器. 这样一个简单的资源管理树就完成了,下面我们说说它的问题: ① 图片和外观和W ...

  6. eclipse中使用loadrunner java api步骤

    1.使用Eclipse新建一个Java工程,名字任意 2.将"%LoadRunner_Home%\classes\lrapi"目录拷贝到工程中 3.将工程导出为Jar包,譬如:命名 ...

  7. js简易猜数字

    Title #div1{ width:400px; height:400px; border:1px solid red; } h1{ width:400px; height:20px; } b#co ...

  8. pyqt小例子 treewidget

    # -*- coding: cp936 -*- from PyQt4.QtCore import * from PyQt4.QtGui import * class InlineEditor(QWid ...

  9. Java 反射 方法调用

    在使用Java 反射时,对方法的调用,可能碰到最多的问题是,方法的变量如何使用.其实,调用方法的变量全部在参数数组里,不管有多少个参数,你都要把它放在参数数组里,如果是单个非数组参数,则可不使用参数数 ...

  10. MongoDB 操作手冊CRUD插入

    插入操作 插入记录 1.插入一条记录 db.testData.insert({num:1,name:'a'}); 结果 WriteResult({ "nInserted" : 1 ...