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. 网站卡死,照惯例运行.bat批量处理文件进行重启不起作用

    网站卡死,照惯例运行.bat批量处理文件进行重启不起作用,进入虚拟机控制台进行虚拟机重启仍然不起作用,通过ping分析物理服务器能ping通,各个虚拟机之前也能ping通,但是不能ping通外部,分析 ...

  2. NSIS检测操作系统x64还是x86的问题。

    想共同维护一个NSIS脚本文件的,不想搞两个版本的脚本文件了.开始想到了!if语句,没试过,不知道行不行得通.后来google了一下.可以用两个头文件搞定.参照下面链接 Reference: http ...

  3. js全局函数

    http://www.w3cschool.cc/jsref/jsref-obj-global.html 以前没搞懂JS的全局函数,全局函数和window对象的函数不一样.全局函数不属于任何一个内置对象 ...

  4. Android SQLite简介

    SQLite 一个非常流行的嵌入式数据库,它支持 SQL 语言,并且只利用很少的内存就有很好的性能.此外它还是开源的,任何人都可以使用它.许多开源项目((Mozilla, PHP, Python)都使 ...

  5. http与https的区别以及https的加密原理

    HTTPS(Secure Hypertext Transfer Protocol)安全超文本传输协议 它是一个安全通信通道,它基于HTTP开发,用于在客户计算机和服务器之间交换信息.它使用安全套接字层 ...

  6. jQuery 各种选择器 $.()用法

    jQuery 元素选择器jQuery 使用 CSS 选择器来选取 HTML 元素. $("p") 选取 <p> 元素. $("p.intro") 选 ...

  7. python学习Processing

    # -*- coding: utf-8 -*-__author__ = 'Administrator'import bisect#排序说明:http://en.wikipedia.org/wiki/i ...

  8. C/C++基础概念

    1.类占用的内存大小: 1)在不同位数的操作系统下,各种数据类型所占用的内存大小:32位和64位操作系统 http://blog.csdn.net/b_zhang/article/details/68 ...

  9. class 类(4)

    要将类实例化,然后通过实例来调用类的方法(函数).在此,把前面经常做的这类事情概括一下: 方法是类内部定义函数,只不过这个函数的第一个参数是self.(可以认为方法是类属性,但不是实例属性) 必须将类 ...

  10. Android TextView中实现点击文本超链接(无下划线)的封装类

    android中有的时候须要在TextView上设置一些超链接,点击这些超链接时进行一些操作.比如新浪微博上的一些keyword,点击时会跳转到对应的页面. 怎样实现我们就直接看源代码吧. /** * ...