当前位置: 首页 > 工具软件 > Gcovr > 使用案例 >

Gcovr 入门指导

顾琛
2023-12-01

Gcovr 入门指导
在上一节Gtest的学习中,我们对Money Demo通过gtest进行了测试,那么我们的 测试覆盖了代码中所有的情况么?测试的完全么?也许我们不想花费太多的时间 纠结在测试上,所以下面推荐一款代码覆盖率的工具(gcovr)以供大家检查自己的 代码覆盖情况。强调一下,我们最好是在linux下学习gcovr的使用。
那么什么是Gcovr呢,我们看一下Gcovr的guide.pdf中是如何描述的:
“Gcovr provides a utility for managing the use of the GNU gcov utility and generating summarized code coverage results. This command is inspired by the Python coverage.py package, which provides a similar utility in Python. The gcovr command produces either compact human-readable summary reports, machine readable XML reports (in Cobertura format) or simple HTML reports. Thus, gcovr can be viewed as a command-line alternative to the lcov utility, which runs gcov and generates an HTML-formatted report.”
Table of Contents
• 1 Gcovr的下载:
• 2 Gcovr的使用:
o 2.1 Makefile
o 2.2 Gcovr的具体命令参数的使用
 2.2.1 -r 参数
 2.2.2 –branches
 2.2.3 XML输出文件
 2.2.4 HTML输出
 2.2.5 –html-details
 2.2.6 Gcovr的其他命令
• 3 Gcovr注意事项
• 4 作业
• 5 参考文献
1 Gcovr的下载:
http://gcovr.com/
上面给出的连接是gcovr的home page。在home page中我们能下载到有关gcovr 的PDF,HTML等一些指导书籍。
大家可以从下面这个链接处下载到Gcvor,下载最新的3.2即可。
https://github.com/gcovr/gcovr/releases
2 Gcovr的使用:
当我们把下载的gcovr-3.2.zip(或者是gcovr.tar.gz)解压缩,比如我的解压缩 的位置是:/home/yulixiang/work/GtestAndGcovr/gcovr-3.2。
我的Money的源码位置是: /home/yulixiang/work/GtestAndGcovr/SourceCode
那么Gcovr和Money源码SourceCode是同级目录。
下面我们还是根据Money例子来讲述如何使用Gcovr。首先我们要知道那怎么才 能在我们的项目中加入Gcovr。
2.1 Makefile
在Money例子的Makefile编译选项中加入编译选项: -fprofile-arcs -ftest-coverage -fPIC -O0。 在链接选项中加入-lgcov选项即可。就是这么 简单!
// 编译选项
CXXFLAGS +=-c -g -Wall -Wextra -I$(GTEST_DIR)/include -fprofile-arcs -ftest-coverage -fPIC -O0

//链接选项
LDFLAGS += -L$(GTEST_DIR)/lib -lgtest -lpthread -lgcov
在加入这些编译和链接选项之后,整个Makefile的代码如下:
GTEST_DIR=your_gtest_directory
SRC_DIR=your_Money_SouceCode_directory

LDFLAGS += -L$(GTEST_DIR)/lib -lgtest -lpthread -lgcov

CXXFLAGS +=-c -g -Wall -Wextra -I$(GTEST_DIR)/include -fprofile-arcs -ftest-coverage -fPIC -O0

TARGETS = money_unittest
OBJS = money.o gtestMoney.o

CC=g++

.PHONY: clean all test

all: $(TARGETS)

$(TARGETS) : $(OBJS)
$(CC) $^ -o $@ $(LDFLAGS)

money.o : $(SRC_DIR)/Money.cpp $(SRC_DIR)/Money.h
$(CC) $(CXXFLAGS) $< -o $@

gtestMoney.o : $(SRC_DIR)/MoneyTest.cpp $(SRC_DIR)/Money.h
$(CC) $(CXXFLAGS) $< -o $@

clean:
rm -f $(TARGETS) $(OBJS)
rm *.gcno
rm *.gcda
rm *.xml

test: ( T A R G E T S ) . / (TARGETS) ./ (TARGETS)./(TARGETS)
在更新Makefile之后我们编译(make)就会有.gcno文件产生,我们执行 money_unittest.exe就会有.gcda文件产生。这两种文件是Gcovr分析的时候 会用到的二进制文件。那现在Gcovr分析所需要的文件已经有了怎么使用 Gcovr来分析Money的代码覆盖率呢?
2.2 Gcovr的具体命令参数的使用
2.2.1 -r 参数
因为上文我把gcovr-3.2文件夹放在与我们的SourceCode是同级目录,所以我 们可以在SourceCode中直接执行下面的命令:
// 执行gcovr
~/work/GtestAndGcovr/SourceCode> python …/gcovr-3.2/scripts/gcovr -r .
就会产生如下的结果

‘-r’ 操作只是产生一个简单的报告。从输出的结果我们可以看到Money.cpp文 件共有13行,但之执行了7行文件。覆盖率仅仅是53% 。那么我们很想知道具体都 有哪些行没有覆盖,这个时候用下列这些命令了。
2.2.2 –branches
–branches命令能够总结出每个项目文件有多少个branches,测试用例中覆盖了 多少个branches。

2.2.3 XML输出文件
Gcovr在默认的条件下是生成一个纯文本的表格。Gcovr能够生成一个XML的输 出文件,使用–xml 和 –xml-pretty
~/work/GtestAndGcovr/SourceCode> python …/gcovr-3.2/scripts/gcovr -r . --xml --xml-pretty
由于输出很多,所以我添加到附件XMLOutput.xml文件中,下面是输出的一部分:

<?xml version="1.0" ?>


. ... –xml产生的XML的格式是Cobertura XMl格式,这种格式支持在Jenkins和Hudson 等服务器上显示。其中–xml命令是产生一个复杂的XML输出,而–xml-pretty命 令是产生一个易读的XML。我们从上面的输出可以知道,XML的输出要比简单的纯 文本输出显示了更多的信息。 2.2.4 HTML输出 gcovr除了能输出xml格式的代码覆盖信息还能输出html格式的文件,通过下 面的命令: ~/work/GtestAndGcovr/SourceCode> python ../gcovr-3.2/scripts/gcovr -r . --html -o result.html 可以产生一个result.html 文件。这里的-o(–output)命令是产生一个名字为 result.html的文件来存储–html命令所产生的HTML输出。双击result.html文件, 就可以看到

–html命令只是产生一个总结所有的文件信息的单一网页。
2.2.5 –html-details
–html-details命令是为每一个项目文件产生一个单独的网页。每一个单独 的网页都包含了该文件详细的代码覆盖情况。
• 注意: 在使用–html-details必须同时使用-o命令。
~/work/GtestAndGcovr/SourceCode> python …/gcovr-3.2/scripts/gcovr -r . --html --html-details -o result.html
执行这个命令后我们可以看到会有:result.html, result.Money.cpp.html, result.Money.h.html和result.MoneyTest.cpp.html四个HTML文件产生。当我们 双击result.html。

可以看到和–html命令一样的页面,但是现在我们能够点击.cpp,跳转到 Money.cpp文件的代码覆盖情况的页面。一般情况下,我们也是最关心.cpp文件的 覆盖情况。

通过这个包含了详细信息的HTML文件,我们可以直观的看到,我们的测试代码没 有覆盖operator==, opreator!= 这两个功能接口,也没有覆盖operator +=中的 一个分支。根据这些详细的信息有助于我们修改测试。
2.2.6 Gcovr的其他命令
通过 gcovr –help 就可以显示出gcovr的命令摘要。
~/work/GtestAndGcovr/SourceCode> python …/gcovr-3.2/scripts/gcovr --help

Usage: gcovr [options]

A utility to run gcov and generate a simple report that summarizes the
coverage

Options:
-h, --help show this help message and exit
–version Print the version number, then exit
-v, --verbose Print progress messages
–object-directory=OBJDIR
Specify the directory that contains the gcov data
files. gcovr must be able to identify the path
between the *.gcda files and the directory where gcc
was originally run. Normally, gcovr can guess
correctly. This option overrides gcovr’s normal path
detection and can specify either the path from gcc to
the gcda file (i.e. what was passed to gcc’s ‘-o’
option), or the path from the gcda file to gcc’s
original working directory.
-o OUTPUT, --output=OUTPUT
Print output to this filename
-k, --keep Keep the temporary *.gcov files generated by gcov. By
default, these are deleted.
-d, --delete Delete the coverage files after they are processed.
These are generated by the users’s program, and by
default gcovr does not remove these files.
-f FILTER, --filter=FILTER
Keep only the data files that match this regular
expression
-e EXCLUDE, --exclude=EXCLUDE
Exclude data files that match this regular expression
–gcov-filter=GCOV_FILTER
Keep only gcov data files that match this regular
expression
–gcov-exclude=GCOV_EXCLUDE
Exclude gcov data files that match this regular
expression
-r ROOT, --root=ROOT Defines the root directory for source files. This is
also used to filter the files, and to standardize the
output.
-x, --xml Generate XML instead of the normal tabular output.
–xml-pretty Generate pretty XML instead of the normal dense
format.
–html Generate HTML instead of the normal tabular output.
–html-details Generate HTML output for source file coverage.
–html-absolute-paths
Set the paths in the HTML report to be absolute
instead of relative
-b, --branches Tabulate the branch coverage instead of the line
coverage.
-u, --sort-uncovered Sort entries by increasing number of uncovered lines.
-p, --sort-percentage
Sort entries by decreasing percentage of covered
lines.
–gcov-executable=GCOV_CMD
Defines the name/path to the gcov executable [defaults
to the GCOV environment variable, if present; else
‘gcov’].
–exclude-unreachable-branches
Exclude from coverage branches which are marked to be
excluded by LCOV/GCOV markers or are determined to be
from lines containing only compiler-generated “dead”
code.
-g, --use-gcov-files Use preprocessed gcov files for analysis.
-s, --print-summary Prints a small report to stdout with line & branch
percentage coverage

3 Gcovr注意事项
我们使用gcovr检查代码的覆盖率,要求是覆盖率越大越好,包括代码branch的 覆盖和line覆盖。
4 作业
通过这一课的学习,要求大家对自己的上一次作业DemoContainer,进行代码覆 盖的检查,并且根据提供的信息,补全我们的代码测试。例如我们的作业可以 达到下面的覆盖结果(gcovr命令用的是gcovr -r SourceCode/ –html –html-details –branch -o result.html)。我们最关心的是.cpp( DemoContainer.cpp)文件的覆盖率,从下面的截图我们可以看出 DemoContainer.cpp文件的line和branch覆盖均为100%。这是最理想的结果。

5 参考文献
http://gcovr.com/
Author: 于丽香 <yu-lx@neusoft.com >
Date: 2018-11-03 15:00:52 CST
HTML generated by org-mode 6.33x in emacs 23

Gtest 测试指导 入门基础(A)
Table of Contents
• 1 Gtest的基本使用,包括下载,安装,编译。
o 1.1 下载
o 1.2 编译
 1.2.1 Gtest静态库的编译
 1.2.2 Gtest在VS中的编译
• 2 在项目中配置Gtest
o 2.1 Gtest在非VS环境下的配置
o 2.2 Gtest在VS环境下的配置
• 3 Gtest的使用
o 3.1 Makefile
o 3.2 构建代码
o 3.3 Gtest断言的使用
o 3.4 Gtest的异常检查
o 3.5 Gtest的事件机制
o 3.6 Gtest的参数化
o 3.7 Gtest的死亡测试
 3.7.1 *_DEATH(statement, regex)
 3.7.2 *_EXIT(statement, predicate, regex)
 3.7.3 死亡测试运行方式
 3.7.4 死亡测试的注意事项
o 3.8 Gtest的运行参数
• 4 作业
o 4.1 编写一个DemoContainer类,按以下接口要求实现:
• 5 参考文献
1 Gtest的基本使用,包括下载,安装,编译。
1.1 下载
直接在google中搜索gtest,第一个就是。也可以从下面地址下载gtest。
https://code.google.com/p/googletest/downloads/
1.2 编译
1.2.1 Gtest静态库的编译
在下载解压后,假设你把gtest源码放在/usr/src/gtest
GTEST_DIR=/usr/src/gtest
设置完GTEST_DIR之后,执行下列的命令
g++ -I G T E S T D I R / i n c l u d e − I {GTEST_DIR}/include -I GTESTDIR/includeI{GTEST_DIR} -c ${GTEST_DIR}/src/gtest-all.cc

ar -rv libgtest.a gtest-all.o
来生成libgtest.a。
1.2.2 Gtest在VS中的编译
下载解压后,里面有个msvc目录,使用VS的同学可以直接的打开msvc里面的工 程文件,打开后会提示你升级,升级后,我们直接编译里面的“gtest”工程, 可以直接编过去的。最好是编译Debug和Relese两个版本。
这里需要注意的是:如果你升级gtest是在VS2008中升级,那么你要使用 gtest 进行测试的demo最好也是VS2008工程,不然你会发现很郁闷,你的 demo怎么也编不过。
如果你编译了Debug和Relese两个版本之后,在msvc里面就有两个文件夹 Debug和Release,这两个目录中能看到编译出来的gtestd.lib或gtest.lib文 件。
2 在项目中配置Gtest
2.1 Gtest在非VS环境下的配置

  1. 把已经生成的libgtest.a放在自己定义的lib中,如 h:/work/myGtest/lib.
  2. 把Gtest需要的头文件放在租户定义的头文件中,如: h:/work/myGtest/include(头文件只需要从Gtest源码中直接copy就可以了)
    2.2 Gtest在VS环境下的配置
    假设我们是用VS2010对gtest进行的编译,那么我们的这个例子也要在VS2010 中建立。
    创建我们要测试的Demo,在VS2010中创建一个空项目,命名为GtestMoney。 接下来为GtestMoney项目配置gtest环境。
  3. 设置gtest头文件路径: 项目 - 属性 - VC++目录 - 包含目录 - 编辑:在 这里添加你的gtest\include 例如我包含目录是: D:\SoftWare\gtest-1.7.0\gtest-1.7.0\include
  4. 设置gtest.lib路径: 项目 - 属性 - 链接器 - 输入 - 附加依赖项: 这里添加的你gtestd.lib/gtest.lib(Debug编译模式用 gtestd.lib,Relese编译用gtest.lib).例如我的附件依赖项是: D:\SoftWare\gtest-1.7.0\gtest-1.7.0\msvc\gtest\Debug\gtestd.lib
  5. Runtime Library设置: 项目 - 属性 - C/C++ - 代码生成器 - 运行库 选择 “多线程调试(/MTD)”。注意:如果Demo和gtest编译用的都是 Relese版本,Runtime Library 设置为"/MT". 当然你可以选择动态链接 (/MD),前提是你之前编译的gtest也同样使用了/MD选项。
    3 Gtest的使用
    下面将介绍在非VS环境下如何使用Gtest,我们将要用一个money的例子来展开 对Gtest的学习。
    3.1 Makefile
    一般的项目要有一个头文件一个cpp文件还有一个测试文件,我们分别命名为 Money.h, Money.cpp和MoneyTest.cpp.因为我们要使用到Gtest,所以添加一 个Makefile来连接Gtest到我们的工程中。Makefile的内容如下。

Should change to your path which contain libgtest.a

GTEST_DIR=H:/work/myGtest

The dirtory which contain Money.h, Money.cpp, MoneyTest and Makefile

SRC_DIR=/cygdrive/h/work/Sample

LDFLAGS += -L ( G T E S T D I R ) / l i b − l g t e s t − l p t h r e a d C X X F L A G S + = − c − g − W a l l − W e x t r a − I (GTEST_DIR)/lib -lgtest -lpthread CXXFLAGS += -c -g -Wall -Wextra -I (GTESTDIR)/liblgtestlpthreadCXXFLAGS+=cgWallWextraI(GTEST_DIR)/include

TARGET = money_unittest
OBJS = money.o gtestMoney.o

CC=g++

.PHONY: clean all test

All Google Test headers. Usually you shouldn’t change this

definition.

all: $(TARGET)

$(TARGET) : $(OBJS)
$(CC) $^ -o $@ $(LDFLAGS)

money.o : $(SRC_DIR)/Money.cpp $(SRC_DIR)/Money.h
$(CC) $(CXXFLAGS) $< -o $@

gtestMoney.o : $(SRC_DIR)/MoneyTest.cpp $(SRC_DIR)/Money.h
$(CC) $(CXXFLAGS) $< -o $@

clean:
rm -f $(TARGET) $(OBJS)

test: ( T A R G E T ) . / (TARGET) ./ (TARGET)./(TARGET)
现在就可以在我们的Money工程中使用Gtest了。
3.2 构建代码

  1. 在我们开发一个新的功能之前,首先我们要确定要"做什么"!比如说我们 现在要实现一个money类,那我们就要首先确定我们这个money类都需要具 备哪些功能和属性。当我们提到money:
  2. 首先想到的是money的数量(amount);
  3. 还会想到money的货币种类(currency),是美元还是人民币;
  4. money与money之间会有比较:这两个money的数量(amount)和货币种类 (currency)是否相等(有 == 和 != 两种可能);
  5. 两个money如果货币种类相同,那么两个money的数量就可以相加。
  6. 当我们确定功能(money)之后要写针对这个功能(还没有具体的代码的 money类)写单元测试例子。单元测试例子要覆盖这个功能(money)的"做什 么"。每个单元测试至少应该有两个测试列子,一个是Negative,一个是 positive。
    首先我们就要开始写一个测试用例来测试我们的money能否实例化成功。
    // file:MoneyTest.cpp

TEST(MoneyConstructorTest, TestConstructor)
{
// Set up
const std::string currencyAA(“AA”);
const double longNumber = 123.456;

// Process
Money money(longNumber, currencyAA);

// Check
// Positive test case
EXPECT_EQ(longNumber, money.getAmount());
EXPECT_STREQ("AA", money.getCurrency().c_str());

}

int main(int argc, char** argv)
{
return 0;
}
这段测试代码是测试一个实例money是否创建成功,money的amount和currency是 否与我们创建实例初始化的值是相同的。
而此时执行命令"make"进行编译肯定报错,因为我们还没有Money这个类也没有接 口函数getAmount()和getCurrency()。所以我们要根据测试用例来补充我们的 Money类。
根据测试用例我们知道,Money类要有一个getAmount()这个功能要得到成员 amount。还有一个getCurrency()得到成员currency,而成员amount和currency通 过Money的构造函数进行初始化。所以接下来在我们的代码中实现这部分功能。
//file:Money.h

#ifndef MONEY_H
#define MONEY_H

#include
#include

class Money
{
public:
Money( double amount, std::string currency )
: m_amount( amount )
, m_currency( currency )
{
}
double getAmount() const;
std::string getCurrency() const;

private:
double m_amount;
std::string m_currency;
};
#endif
// file Money.cpp

#include “Money.h”

double Money::getAmount() const
{
return m_amount;
}

std::string Money::getCurrency() const
{
return m_currency;
}
这时编译(make)还是编译不过,因为我们没有运行所有的测试用例。那我们用什 么运行测试呢?在MoneyTest.cpp的main函数中添加"RUN_ALL_TESTS()"意思是: 运行所有测试案例。
// file MoneyTest.cpp
int main(int argc, char** argv)
{

return RUN_ALL_TESTS();

}
这个时候编译(make)就会通过。但是执行的时候(make test)会产生下面的错误:
lixiang@lixiang-PC /cygdrive/h/work/Sample
$ make test
./money_unittest

This test program did NOT call ::testing::InitGoogleTest before calling RUN_ALL_
TESTS(). Please fix it.
Makefile:36: recipe for target ‘test’ failed
make: *** [test] Error 1

通过错误我们可以看到需要在MoneyTest.cpp的main函数中加入 testing::InitGoogleTest 所以我们需要继续修改MoneyTest.cpp中的main:
// file MoneyTest.cpp
int main(int argc, char** argv)
{
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
这时我们make编译成功。运行money_unittest.exe会有如下输出:
好,到目前为止我们已经完成了Gtest的环境的搭配和基本的构建使用。下面就来 具体的学习一下Gtest针对不同情况的测试吧。
3.3 Gtest断言的使用
上面就是一个简单的测试案例。这个我们使用了TEST这个gtest包装好的宏, 它有两个参数,官方的解释:#define TEST(test_case_name, test_name) GTEST_TEST(test_case_name, test_name)。
对检查点的检查,我们上面使用到了EXPECT_EQ和EXPECT_STREQ这两个宏, 其中EXPECT_EQ这个宏用来比较两个数值是否相等。 EXPECT_STREQ(expected_str, actual_str)是比较两个C字符串内容(同时 支持char* 和 wchar_t*).
Gtest采用了大量的宏来包装断言,此断言不同于C语言的断言(assert),按照 使用方法分为两类:

  1. EXPECT_* 系列断言失败时,案例继续往下执行。

  2. ASSERT_* 系列断言失败时,退出当前函数,ASSERT_*后面的代码不会被 执行.(注意:并不是退出当前案例)
    在上面的列子中为了我们的案例能够运行起来我们在main函数中添加如下代 码:
    testing::InitGoogleTest(&argc, argv);
    因为gtest的测试用例允许接收一系列的命令行参数因此我们在 "testing::InitGoogleTest(&argc, argv);"中将命令行参数传给gtest, gtest的命令行参数非常的丰富,有兴趣的大家可以自己查阅资料详细的了解 一下。
    下面我们添加一个新的断言:
    // file: MoneyTest.cpp
    TEST(MoneyConstructorTest, TestConstructor)
    {
    // Set up
    const std::string currencyAA(“AA”);
    const double longNumber = 123.456;

    // Process
    Money money(longNumber, currencyAA);

    // Check
    EXPECT_EQ(longNumber, money.getAmount());
    EXPECT_STREQ(“AA”, money.getCurrency().c_str());

    EXPECT_EQ(555, money.getAmount());

    std::cout << “Code after EXPECT!” << std::endl;
    }
    会出现什么结果呢?

这样的结果将会产生!这是因为EXPECT_EQ第一个参数是我们期望的结果是数值 555,而实际的结果是123.456,这与我们的期望不同,所以会有error产生。
值得注意的是在"EXPECT_EQ(555, money.getAmount())“出现error之后仍然继 续执行输出"Code after EXPECT!”.
如果我们期望结果数值不是实际函数的结果数值,那么就用下面的语句。
EXPECT_NE(555, money.getAmount());
这个时候所有的测试就会通过了!
让我们再加上一个ASSERT_*系列的断言,来体会ASSERT与EXPECT区别。
// file MoneyTest.cpp

// Assert test
TEST(MoneyConstructorTest, TestConstructorAssert)
{
// Set up
const std::string currencyBB(“ASSERT”);
const double longNumber = 222.222;

// Process
Money money(longNumber, currencyBB);

// Check
ASSERT_STREQ("AA", money.getCurrency().c_str());

std::cout << "Code after ASSERT!" << std::endl;

}

运行结果是

在ASSERT失败之后, 语句"Code after ASSERT"没有出现! 所以我们注意,最好不要在ASSERT语句后面做释放内存等操作!
现在我们只是依照Money的功能和功能对应的测试用例添加了Money类的构造函数 和接口getAmount(), getCurrency的源码,剩下的接口分别是operator ==, operator != 和 operator +=,大家可以按照想好功能,为功能添加测试用例, 补充功能源码的方式来完成对以上三个未完成的接口的测试。下面是参考代码:
// file Money.h

#ifndef MONEY_H
#define MONEY_H

#include
#include

class IncompatibleMoneyError : public std::runtime_error
{
public:
IncompatibleMoneyError() : std::runtime_error( “Incompatible moneys” )
{
}
};

class Money
{
public:
Money( double amount, std::string currency )
: m_amount( amount )
, m_currency( currency )
{
}

double getAmount() const;

std::string getCurrency() const;

bool operator ==( const Money &other ) const;

bool operator !=( const Money &other ) const;

Money &operator +=( const Money &other );

private:
double m_amount;
std::string m_currency;
};
#endif
// file Money.cpp

#include “Money.h”

double Money::getAmount() const
{
return m_amount;
}

std::string Money::getCurrency() const
{
return m_currency;
}

bool Money::operator ==( const Money &other ) const
{
return m_amount == other.m_amount &&
m_currency == other.m_currency;
}

bool Money::operator !=( const Money &other ) const
{
return !(*this == other);
}

Money & Money::operator +=( const Money &other )
{
if ( m_currency != other.m_currency )
throw IncompatibleMoneyError();

m_amount += other.m_amount;
return *this;

}

大家都可以用EXPECT_TRUE(FALSE)/ASSERT_TRUE(FALSE)书写出对这些函数接口 的测试相应的测试。
gtest的断言按照常用功能依次分为12类,平常主要用到的是一下几类:
|ASSERT_FALSE(condition) |EXPECT_TRUE(condition) |condition == false | |ASSERT_NE(val1, val2) | EXPECT_NE(val1, val2) | val1 != val2 |
布尔值比较
ASSERT_TRUE(condition) EXPECT_TRUE(condition) condition == true
数值型数据比较
ASSERT_EQ(expected, actual) EXPECT_EQ(expected, actual) expected == actual
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) val2 >= val2
字符串比较
ASSERT_STREQ(str1, str2) EXPECT_STREQ(str1, str2) 两个C字符串内容相同(同时支持char *和wchart *类型)
ASSERT_STRNE(str1, str2) EXPECT_STRNE(str1, str2) 两个C字符串内容不同(同时支持char *和wchart *类型)
ASSERT_STRCASEEQ(str1,str2) EXPECT_STRCASEEQ(str1,str2) 两个C字符串内容相同,忽略大小写(只支持char *类型)
ASSERT_STRCASENE(str1,str2) EXPECT_STRCASENE(str1,str2) 两个C字符串内容不同,忽略大小写(只支持char *类型)
浮点数比较
ASSERT_FLOAT_EQ(val1,val2) EXPECT_FLOAT_EQ(val1,val2) the two float values are almost equal
ASSERT_DOUBLE_EQ(val1,val2) EXPECT_DOUBLE_EQ(val1,val2) the two double values are almost equal
近似数比较
ASSERT_NEAR(val1, val2, abs_error) EXPECT_NEAR(val1, val2, abs_error 两个数值val1和val2的 绝对值差不超过 abs_error
异常检查
ASSERT_THROW(statement, exception_type) EXPECT_THROW(statement, exception_type) 抛出指定类型异常
ASSERT_THROW(statement) EXPECT_THROW(statement) 抛出任意类型异常
ASSERT_NO_THROW(statement) EXPECT_NO_THROW(statement) 不抛异常
函数值与参数检查(目前最多只支持5个参数)
ASSERT_PRED1(pred1, val1) EXPECT_PRED1(pred1, val1) pred1(val1) returns true
ASSERT_PRED2(pred2, val1,val2) EXPECT_PRED2(pred2, val1, val2) pred2(val1, val2) returns true
Windows HRESULT
ASSERT_HRESULT_SUCCEEDED (expression) EXPECT_HRESULT_SUCCEEDED (expression) expression is a success HRESULT
ASSERT_HRESULT_FAILED (expression) EXPECT_HRESULT_FAILED (expression) expression is a failure HRESUL
自定义格式函数与参数检查(目前最多支持5个参数)
ASSERT_PRED_FORMAT1(pred1, val1) EXPECT_PRED_FORMAT1(pred1, val1) pred1(val1) is successful
ASSERT_PRED_FORMAT1(pred1, val1, val2) EXPECT_PRED_FORMAT1(pred1, val1, val2) pred2(val1, val2) is successful
3.4 Gtest的异常检查
通过上面的表格我们知道gtest能够检测异常状态,异常检查一般用到的是 ASSERT_THROW/EXPECT_THROW。那么我们就来增加一个异常检查的测试。
// file MoneyTest.cpp

TEST(MoneyThrowTest, ThrowTest)
{
// Set up
const Money money123AA(123, “AA”);

// Process
Money money123BB(123, "BB");

EXPECT_ANY_THROW(money123BB += money123AA);
EXPECT_THROW(money123BB += money123AA, IncompatibleMoneyError);

}
那么现在的输出结果是:

MoneyThrowTest.ThrowTest显示的状态是通过,证明已经有异常抛出,测试用例 通过。
3.5 Gtest的事件机制
在开始gtest的事件机制之前,我们需要了解一个概念:测试固件!
很多时候,我们想在不同的测试执行前创建相同的配置环境,在测试执行结束后 执行相应的清理工作,测试固件(Test Fixture)为这种需求提供了方便。在单 元测试中,Fixture的作用是为测试创建辅助性的上下文环境,实现测试的初始化 和终结与测试过程本身的分离,便于不同测试使用相同代码来搭建固定的配置环 境。用体操比赛的说法,测试过程体现了特定测试的自选动作,测试固件则体现 了对一系列测试(在开始和结束时)的规定动作。有些讲单元测试的书籍直接把 测试固件称为Scaffolding(脚手架)。使用测试固件比单纯调用TEST宏稍微麻烦 一些:

  1. 从gtest的testing::Test类派生一个类,用public或protected定义 以下所有成员。
  2. (可选)建立环境:使用默认构造函数,或定义一个虚成员函数 virtual void SetUp()。
  3. (可选)销毁环境:使用析构函数,或定义一个虚成员函数virtual void TearDown()。
  4. 用TEST_F定义测试,写法与TEST相同,但测试用例名必须为上面定义 的类名。
    每个带固件的测试的执行顺序是:
  5. 调用默认构造函数创建一个新的带固件对象。
  6. 立即调用SetUp函数。
  7. 运行TEST_F体。
  8. 立即调用TearDown函数。
  9. 调用析构函数销毁类对象。
    Gtest提供了多种事件机制方便在测试用例之前或者完成以后进行一些操作,
    通过这些操作,我们可以在各个测试用例之前共享一些通用方法,共享资源。使 我们的用例更加的简洁,清晰。Gtest的事件机制按照使用方法分为3类:
  10. 全局事件:在所有测试用例执行之前和完成之后生效。可以在全局事件中完成 一些测 试环境的初始化和资源回收工作,比如预留内存申请/回收,组件对象 初始化/析构等。 注意: 资源的初始化放在SetUp()中。资源的回收放在TearDown()中。
  11. TestSuite级别:在指定的测试套第一个测试用例之前,最后一个测试用例之 后。如果根据子模块定义测试套,那么就可以在TestSuite事件中完成一些子 模块的线程、消息队列等的初始化和资源回收工作。
  12. TestCase级别:在每个测试用例执行前后,即在每个测试代码的断言前后进行 执行。
    下面我们就分别介绍这3类事件的用法:
    • 全局事件
    必须通过继承testing::Environment类,实现里面的SetUp和TearDown方 法:
  13. SetUp()方法在所有用例执行前执行;
  14. TearDown()方法在所有用例执行后执行;
    在我们的MoneyTest.cpp中添加一个此事件的代码是:
    // file MoneyTest.cpp

class MoneyTestEnvironment : public testing::Environment
{
public:
virtual void SetUp()
{
std::cout << “Money Golbal Event SetUp.” << std::endl;
}

virtual void TearDown()
{
    std::cout << "Money Global Event TearDown" << std::endl;
}

};
完成继承类方法实现以后,还需要告诉gtest添加全局事件,我们需要在main函数 中通过testing::AddGlobalTestEnvironment方法添加该全局事件。如果需要增加 全局事件,也可以写多个继承类,然后将事件都添加到测试用例之前。
int main(int argc, char** argv)
{
// Add the global test environment
testing::AddGlobalTestEnvironment(new MoneyTestEnvironment);

testing::InitGoogleTest(&argc, argv);

return RUN_ALL_TESTS();

}
运行的结果是:

从结果中我们可以看到Setup()在所有测试用例开始之前就自动被调用,而 TearDown()是在所有测试用例执行之后被调用。所以一些全局的资源初始化放在 SetUp()中,全局的资源回收放在TearDown()中。
• TestSuite事件
在某些情况下,我们可能不需要在整个全局的范围内有一些共享而需要在各个测 试间共享一个相同的环境来保存和传递状态,或者环境的状态是只读的,可以只 初始化一次,再或者创建环境的过程开销很高,要求只初始化一次。共享某个固 件环境的所有测试合称为一个“测试套件”(Test Suite),gtest中利用静态成 员变量和静态成员函数实现这个概念:

  1. (可选)在testing::Test的派生类中,定义若干静态成员变量来维 护套件的状态。
  2. (可选)建立共享环境:定义一个静态成员函数static void SetUpTestCase()。
  3. (可选)销毁共享环境:定义一个静态成员函数static void TearDownCase()。
    特别注意:
  4. SetUpTestCase()方法在TestSuit的第一个TestCase之前执行;
  5. TearDownTestCase()方法在TestSuite的最后一个TestCase之后执行;
    在MoneyTest.cpp中添加TestSuite事件的代码如下:
    // file MoneyTest.cpp

// Test Suite Event Test
class MoneySuiteEventTest : public testing::Test
{
public:
static void SetUpTestCase()
{
std::cout << “Money Test SuiteEventTest SetUpTestCase.” << std::endl;
}

static void TearDownTestCase()
{
    std::cout << "Money Test SuiteEventTest TearDownTestCase." << std::endl;
}

};

TEST_F(MoneySuiteEventTest, SuiteTestCaseOne)
{
const std::string currencyCC(“SuiteTestCaseOne”);
const double longNumber = 123.456;

// Process
Money money(longNumber, currencyCC);

// Check
EXPECT_EQ(longNumber, money.getAmount());
EXPECT_STREQ("SuiteTestCaseOne", money.getCurrency().c_str());

}

TEST_F(MoneySuiteEventTest, SuiteTestCaseTwo)
{
const std::string currencyCC(“SuiteTestCaseTwo”);
const double longNumber = 123.456;

// Process
Money money(longNumber, currencyCC);

// Check
EXPECT_EQ(longNumber, money.getAmount());
EXPECT_STREQ("SuiteTestCaseTwo", money.getCurrency().c_str());

}
特别注意使用TEST_F宏的第一个参数必须是上面继承testing::test的继承类名字 (MoneySuiteEventTest)。
运行的结果是:

从输出可以看出SetUpTestCase()是在SuitTestCaseOne之前调用而 TearDownTestCase()是在SuiteTestCaseTwo后调用。
• TestCase事件
与TestSuite事件实现方法相同,需要通过继承testing::Test类,但是只需 要实现里面的SetUp和TearDown两个方法

  1. SetUp()方法在每个TestCase之前执行
  2. TearDown()方法在每个TestCase之后执行;
    在MonyTest.cpp中TestCase事件的代码是:
    // file MoneyTest.cpp

// Test TestCase
class MoneyTestCaseTest : public testing::Test
{
public:
void SetUp()
{
std::cout << “Money Test MoneyTestCaseTest SetUp.” << std::endl;
}

void TearDown()
{
    std::cout << "Money Test MoneyTestCaseTest TearDown." << std::endl;
}

};

TEST_F(MoneyTestCaseTest, TestCaseTestOne)
{
const std::string currencyDD(“TestCaseTestOne”);
const double longNumber = 123.456;

// Process
Money money(longNumber, currencyDD);

// Check
EXPECT_EQ(longNumber, money.getAmount());

}

TEST_F(MoneyTestCaseTest, TestCaseTestTwo)
{
const std::string currencyDD(“TestCaseTestTwo”);
const double longNumber = 123.456;

// Process
Money money(longNumber, currencyDD);

// Check
EXPECT_EQ(longNumber, money.getAmount());

}
运行的结果是

3.6 Gtest的参数化
在设计测试案例时,经常需要考虑给被测函数传入不同的值的情况,以前的做 法一般是写一个通用方法,然后编写在测试案例调用它,即使使用了通用方法, 也需要很多重复性的工作。以下是一般的测试方法,如果需要测试N个数字, 则需要拷贝复制粘贴N次.
Gtest在这里提供了一个灵活的函数参数化测试的方案:

  1. 告诉gtest参数类型:必须添加一个继承类testing::TestWithParam, 其中T就是需 要参数化的参数类型。以上面为例,需要参数化一个int型 的参数;
  2. 参数类型确定以后,需要使用一个新的宏TSET_P进行测试用例,在 TEST_P宏里,使 用GetParam()方法获取当前的参数的具体值;
  3. 使用INSTANTIATE_TEST_CASE_P宏来确定测试的参数取值范围.其中第 一个参数是测试用例的前缀,可以任意取,第二个参数是测试案例的名称, 需要和之前定义的参数化的类的名字相同。第三个参数可以理解成参数生 产器。
    在MoneyTest.cpp中Gtest的参数化代码是:
    // file MoneyTest.cpp

// Parameter Testing one
class MoneyParameterTest : public testing::TestWithParam
{
public:
};

TEST_P(MoneyParameterTest, ParameterTest)
{
double acount = GetParam();
Money money(acount, “Test”);
EXPECT_EQ(acount, money.getAmount());
}

INSTANTIATE_TEST_CASE_P(ParameterTest, MoneyParameterTest,
testing::Values(111.111, 222.2222, 333.3333, 444.44444));
运行的结果是:

Gtest中除了上面testing::Values()数据生成器之外,还有一系列的参数生成器:
Gtest中的参数生成器
Range(begin, end[, step]) 范围在begin~end之间,步长为step,不包括end
Values(v1, v2, …, vN) v1,v2到vN的值
ValuesIn(container) and ValuesIn(begin, end) 从一个C类型的数组或是STL容器,或是迭代器中取值
Bool() 取false 和 true 两个值
Combine(g1, g2, …, gN) 这个比较强悍,它将g1,g2,…gN进行排列组合,g1,g2,…gN本身是一个参数生成器,每次分别从g1,g2,…gN中各取出一个值,组合成一个元组(Tuple)作为一个参数。说明:这个功能只在提供了<tr1/tuple>头的系统中有效。gtest会自动去判断是否支持tr/tuple,如果你的系统确实支持,而gtest判断错误的话,你可以重新定义宏GTEST_HAS_TR1_TUPLE=1。
除了测试用例可以参数化以外,gtest还提供了针对各种不同类型数据时的方案, 以及参数化类型的方案

  1. 首先需要定义一个模版类,从testing::Test类继承
  2. 然后再定义需要测试的具体数据类型,比如下面定义了测试char, int和 unsigned int类型;
  3. 又是一个新的宏,来完成我们的测试案例,在声明模版的数据类型时,使用 TypeParam
    // file: MoneyTest.cpp

// Parameter Testing two

// Step one
template
class MoneyTemplateTest : public testing::Test
{
public:
typedef std::list List;
static T shared;
T value;
};

// Step two
typedef testing::Types<double, long double> MoneyTypes;
TYPED_TEST_CASE(MoneyTemplateTest, MoneyTypes);

// Step three
TYPED_TEST(MoneyTemplateTest, TemplateTest)
{
// Inside a test, refer to the special name TypeParam to get the type
// parameter. Since we are inside a derived class template, C++ requires
// us to visit the members of MoneyTemplateTest via ‘this’.
TypeParam n = this->value;

// To refer to typedefs in the fixture, add the 'typename TestFixture::'
// prefix.  The 'typename' is required to satisfy the compiler.
typename TestFixture::List values;
values.push_back(n);

}

运行结果:

3.7 Gtest的死亡测试
“死亡测试”名字比较恐怖,这里的“死亡”是指程序的崩溃。通常在测试过 程中,我们需要考虑各种各样的输入,有的输入可能直接导致程序崩溃,这时 我们就需要检查程序是否按照预期的方式挂掉,这也就是所谓的“死亡测试”。 gtest的死亡测试能做到在一个安全的环境下执行崩溃的测试案例,同时又对 崩溃结果进行验证
死亡测试宏定义
ASSERT_DEATH(statement, regex) EXPECT_DEATH(statement, regex) statement crashes with the given error
ASSERT_EXIT(statement, predicate, regex) EXPECT_EXIT(statement, predicate, regex) statement exits with the given error and its exit code matches predicate
3.7.1 _DEATH(statement, regex)
由于有些异常只在Debug下抛出,因此还提供了
_DEBUGDEATH,用来处理 Debug和Realease下的不同。 简单来说,通过*_DEATH(statement, regex) 和*_EXIT(statement, predicate, regex),我们可以非常方便的编写导致崩溃 的测试案例,并且在不影响其他案例执行的情况下,对崩溃案例的结果进行检 查。 以下是*DEATH用法介绍:

  1. statement是被测试的代码语句,这里可以使用表达式,也可以直接调用函 数结果;
  2. regex是一个正则表达式,用来匹配异常时在stderr中输出的内容;
  3. 编写死亡测试案例时,TEST的第一个参数,即testcase_name,请使用 DeathTest后缀。 原因是gtest会优先运行死亡测试案例,应该是为线程安 全考虑。
    // file: MoneyTest.cpp

// Death Test
TEST(MoneyDeathTest, DeathTest)
{
// Set up
Money moneyAA(123.456, “DeathTestOne”);
Money moneyBB(0.123456, “DeathTestTwo”);

// Process
EXPECT_DEATH(moneyAA+=moneyBB ,"Test MoneyDeathTest");

}
死亡测试的结果是:

从运行的结果我们可以看到"死亡测试"是第一个被执行的测试!
3.7.2 *_EXIT(statement, predicate, regex)
• 1. statement
• 2. predicate 在这里必须是一个委托,接收int型参数,并返回bool。只有当返回值为true时,死亡测试案例才算通过。
gtest提供了一些常用的predicate:

  1. testing::ExitedWithCode(exit_code) : 如果程序正常退出并且退出码与 exit_code相同则返回true。
  2. testing::KilledBySignal(signal_number) // Windows下不支持 :如果 程序被signal_number信号kill的话就返回true
    • 3. regex是一个正则表达是,用来匹配异常时在stderr中输出的内容。
    在POSIX系统(Linux, Cygwin, 和 Mac)中,gtest的死亡测试中使用的是 POSIX风格的正则表达式,想了解POSIX风格表达式可参考:
  3. POSIX extended regular expression
  4. Wikipedia entry:
    在Windows系统中,gtest的死亡测试中使用的是gtest自己实现的简单的正 则表达式语法。 相比POSIX风格,gtest的简单正则表达式少了很多内容, 比如 (“x|y”), ("(xy)"), ("[xy]") 和(“x{5,7}”)都不支持。
    3.7.3 死亡测试运行方式
    • 1. fast方式(默认方式)

• testing::FLAGS_gtest_death_test_style = “fast”;
• threadsafe方式

• testing::FLAGS_gtest_death_test_style = “threadsafe”;
你可以在 main() 里为所有的死亡测试设置测试形式,也可以为某次测试单 独设置。Google Test会在每次测试之前保存这个标记并在测试完成后恢复, 所以你不需要去管这部分工作 。如:
int main(int argc, char** argv)
{
// Add the global test environment
testing::AddGlobalTestEnvironment(new MoneyTestEnvironment);

testing::InitGoogleTest(&argc, argv);

testing::FLAGS_gtest_death_test_style = "fast";

return RUN_ALL_TESTS();

}
3.7.4 死亡测试的注意事项

  1. 不要在死亡测试里释放内存。
  2. 在父进程里再次释放内存。
  3. 不要在程序中使用内存堆检查。
    3.8 Gtest的运行参数
    使用gtest编写的测试案例通常本身就是一个可执行文件,因此运行起来非常 方便。同时,gtest也为我们提供了一系列的运行参数(环境变量、命令行参 数或代码里指定),使得我们可以对案例的执行进行一些有效的控制。gtest 提供了三种设置的途径:
  4. 系统环境变量
  5. 命令行参数
  6. 代码中指定FLAG
    其优先级原则是,最后设置的那个会生效。通常情况下的优先级顺序为: 命令行参数 > 代码中指定FLAG > 系统环境变量。由于在gtest工程main函数 中,gtest通过testing::InitGoogleTest方法直接处理输入参数,因此测试用 例可以处理命令行参数。
    代码:
    // MoneyTest.cpp

int main(int argc, char** argv)
{
// Add the global test environment
testing::AddGlobalTestEnvironment(new MoneyTestEnvironment);

testing::InitGoogleTest(&argc, argv);

testing::FLAGS_gtest_death_test_style = "fast";

return RUN_ALL_TESTS();

}
这样就拥有了接收和响应gtest工程命令行参数的能力。如果需要在代码中指定 FLAG,可以使用testing::GTESTFLAG这个宏来设置。比如相对于命令行参 数–gtest_output,可以使用testing::GTEST_FLAG(output) = “xml:”;来设置。 注意这里不需要加–gtest前缀了,同时,推荐将这句放置InitGoogleTest之前, 这样就可以使得对于同样的参数,命令行参数优先级高于代码中指定。
// MoneyTest.cpp
int main(int argc, char** argv)
{
// Output farmat is XML
testing::GTEST_FLAG(output) = “xml:”;

// Add the global test environment
testing::AddGlobalTestEnvironment(new MoneyTestEnvironment);

testing::InitGoogleTest(&argc, argv);

testing::FLAGS_gtest_death_test_style = "fast";

return RUN_ALL_TESTS();

}
如果需要gtest的设置系统环境变量,必须注意的是:

  1. 系统环境变量全大写,比如对于–gtest_output,相应的系统环境变量为 GTEST_OUTPUT
  2. 有一个命令行参数例外,那就是–gtest_list_tests,它是不接受系统环境变 量的,只是用 来罗列测试案例名称。
    以下是所有命令行参数列表:
  3. 测试案例集合:
    命令行参数 说明
    –gtest_list_tests 使用这个参数时,将不会执行里面的测试案例,而是输出一个案例的列表。
    –gtest_filter 对执行的测试案例进行过滤,支持通配符 ? 单个字符 * 任意字符 - 排除,如-a 表示除了a : 取或,如a:b 表示a或b 比如下面的例子: ./foo_test 没有指定过滤条件,运行所有案例; ./foo_test –gtest_filter=* 使用通配符*,表示运行所有案例; ./foo_test –gtest_filter=FooTest.* 运行所有“测试案例名称(testcase_name)”为FooTest的案例; ./foo_test –gtest_filter=Null:Constructor 运行所有“测试案例名称(testcase_name)”或“测试名称(test_name)”包含Null或Constructor的案例; ./foo_test –gtest_filter=-DeathTest. 运行所有非死亡测试案例; ./foo_test –gtest_filter=FooTest.*-FooTest.Bar 运行所有“测试案例名称(testcase_name)”为FooTest的案例,但是除了FooTest.Bar这个案例
    –gtest_also_run_disabled_tests 执行案例时,同时也执行被置为无效的测试案例。关于设置测试案例无效的方法为:在测试案例名称或测试名称中添加DISABLED前缀。
    –gtest_repeat=[COUNT] 设置案例重复运行次数: –gtest_repeat=1000:重复执行1000次,即使中途出现错误; –gtest_repeat=-1: 无限次数执行 –gtest_repeat=1000 –gtest_break_on_failure:重复执行1000次,并且在第一个错误发生时立即停止,这个功能对调试非常有用。 –gtest_repeat=1000 –gtest_filter=FooBar:重复执行1000次测试案例名称为FooBar的案例。
  4. 测试案例输出
    命令行参数 说明
    –gtest_color(yes/no/auto) 输出命令行时是否使用一些五颜六色的颜色,默认是 auto。
    –gtest_print_time 输出命令行时是否打印每个测试案例的执行时间,默认是不打印的。
    –gtest_output= xml[:DIRECTORYPATH/:FILEPA\TH] 将测试结果输出到一个xml中。 –gtest_output=xml: 不指定输出路径时,默认为案例 当前路径。 –gtest_output=xml:d:\ 指定输出到某个目录 –gtest_output=xml:d:\foo.xml 指定输出到d:\foo.xml 如果不是指定了特定的文件路径,gtest每次输出的报告不会覆盖,而会以数字后缀的方式创建
  5. 对案例的异常处理
    命令行参数 说明
    –gtest_break_on_failure 调试模式下,当案例失败时停止,方便调试。
    –gtest_throw_on_failure 当案例失败时以C++异常的方式抛出。
    –gtest_catch_exceptions 是否捕捉异常。gtest默认是不捕捉异常的,因此假如测试案例抛了一个异常,很可能会弹出一个对话框,这非常的不友好,同时也阻碍了测试案例的运行。如果想不弹这个框,可以通过设置这个参数来实现。如将–gtest_catch_exceptions设置为一个非零的数。 注意:该参数只在Windows下有效。
    以下是命令行参数列表在使用过程中遇到的一些问题总结:
  6. 同时使用–gtest_filter和–gtest_output=xml:时,在xml测试报告中能否 只包含过滤后的测试案例的信息。
  7. 有时在代码中设置 testing::GTEST_FLAG(catch_exceptions) = 1和在命 令行中使用–gtest_catch_exceptions结果稍有不同,在代码中设置 FLAG方式有时候捕捉不了某些异常,但是通过命令行参数的方式一般都不会 有问题。最后处理办法是既在代码中设置FLAG,又在命令行参数中传 入–gtest_catch_exceptions,估计是gtest在catch_exceptions方面不 够稳定的原因导致。
    到目前为止,我们已经掌握了Gtest常用的测试方法。Money例子的整体的 测试代码参考如下:
    // file: MoneyTest.cpp

#include
#include
#include <gtest\gtest.h>
#include “Money.h”

// EXPECT Testing
TEST(MoneyConstructorTest, TestConstructor)
{
// Set up
const std::string currencyAA(“AA”);
const double longNumber = 123.456;

// Process
Money money(longNumber, currencyAA);

// Check
EXPECT_EQ(longNumber, money.getAmount());
EXPECT_STREQ("AA", money.getCurrency().c_str());

//EXPECT_EQ(555, money.getAmount());

std::cout << "Code after EXPECT!" << std::endl;

}
// End EXPECT Testing

// ASSERT Testing
TEST(MoneyConstructorTest, TestConstructorAssert)
{
// Set up
const std::string currencyBB(“ASSERT”);
const double longNumber = 222.222;

// Process
Money money(longNumber, currencyBB);

// Check
ASSERT_STREQ("AA", money.getCurrency().c_str());

std::cout << "Code after ASSERT!" << std::endl;

}
// End ASSERT Testing

// Throw Testing
TEST(MoneyThrowTest, ThrowTest)
{
// Set up
const Money money123AA(123, “AA”);

// Process
Money money123BB(123, "BB");


EXPECT_ANY_THROW(money123BB += money123AA);

EXPECT_THROW(money123BB += money123AA, IncompatibleMoneyError);

}
// End Throw Testing

// Global Event Testing
class MoneyTestEnvironment : public testing::Environment
{
public:
virtual void SetUp()
{
std::cout << “Money Global Event SetUp.” << std::endl;
}

virtual void TearDown()
{
    std::cout << "Money Global Event TearDown" << std::endl;
}

};
// End Global Event Testing

// Suite Event Testing
class MoneySuiteEventTest : public testing::Test
{
public:
static void SetUpTestCase()
{
std::cout << “Money Test SuiteEventTest SetUpTestCase.” << std::endl;
}

static void TearDownTestCase()
{
    std::cout << "Money Test SuiteEventTest TearDownTestCase." << std::endl;
}

};

TEST_F(MoneySuiteEventTest, SuiteTestCaseOne)
{
const std::string currencyCC(“CC”);
const double longNumber = 123.456;

// Process
Money money(longNumber, currencyCC);

// Check
EXPECT_EQ(longNumber, money.getAmount());
EXPECT_STREQ("CC", money.getCurrency().c_str());

}

TEST_F(MoneySuiteEventTest, SuiteTestCaseTwo)
{
const std::string currencyCC(“DD”);
const double longNumber = 123.456;

// Process
Money money(longNumber, currencyCC);

// Check
EXPECT_EQ(longNumber, money.getAmount());
EXPECT_STREQ("DD", money.getCurrency().c_str());

}
// End Suite Event Testing

// TestCase Event Testing
class MoneyTestCaseTest : public testing::Test
{
public:
void SetUp()
{
std::cout << “Money Test MoneyTestCaseTest SetUp.” << std::endl;
}

void TearDown()
{
    std::cout << "Money Test MoneyTestCaseTest TearDown." << std::endl;
}

};

TEST_F(MoneyTestCaseTest, TestCaseTestOne)
{
const std::string currencyDD(“TestCaseTestOne”);
const double longNumber = 123.456;

// Process
Money money(longNumber, currencyDD);

// Check
EXPECT_EQ(longNumber, money.getAmount());

}

TEST_F(MoneyTestCaseTest, TestCaseTestTwo)
{
const std::string currencyDD(“TestCaseTestTwo”);
const double longNumber = 123.456;

// Process
Money money(longNumber, currencyDD);

// Check
EXPECT_EQ(longNumber, money.getAmount());

}
// End TestCase Event Testing

// Parameter Testing one
class MoneyParameterTest : public testing::TestWithParam
{
public:
};

TEST_P(MoneyParameterTest, ParameterTest)
{
double acount = GetParam();
Money money(acount, “Test”);
EXPECT_EQ(acount, money.getAmount());
}

INSTANTIATE_TEST_CASE_P(ParameterTest, MoneyParameterTest,
testing::Values(111.111, 222.2222, 333.3333, 444.44444));
// End Parameter Testing one

// Parameter Extual Testing two
// Step one
template
class MoneyTemplateTest : public testing::Test
{
public:
typedef std::list List;
static T shared;
T value;
};

// Step two
typedef testing::Types<double, long double> MoneyTypes;
TYPED_TEST_CASE(MoneyTemplateTest, MoneyTypes);

// Step three
TYPED_TEST(MoneyTemplateTest, TemplateTest)
{
// Inside a test, refer to the special name TypeParam to get the type
// parameter. Since we are inside a derived class template, C++ requires
// us to visit the members of MoneyTemplateTest via ‘this’.
TypeParam n = this->value;

// To refer to typedefs in the fixture, add the 'typename TestFixture::'
// prefix.  The 'typename' is required to satisfy the compiler.
typename TestFixture::List values;
values.push_back(n);

}
// End Parameter Extual Testing two

// Death Test
TEST(MoneyDeathTest, DeathTest)
{
// Set up
Money moneyAA(123.456, “DeathTestOne”);
Money moneyBB(0.123456, “DeathTestTwo”);

// Process
EXPECT_DEATH(moneyAA+=moneyBB ,"Test MoneyDeathTest");

}
// End Death Test

int main(int argc, char** argv)
{
testing::GTEST_FLAG(output) = “xml:”;

// Add the global test environment
testing::AddGlobalTestEnvironment(new MoneyTestEnvironment);

testing::InitGoogleTest(&argc, argv);

testing::FLAGS_gtest_death_test_style = "fast";

return RUN_ALL_TESTS();

}

4 作业
4.1 编写一个DemoContainer类,按以下接口要求实现:

  1. int get_size(void)
    get_size函数的功能是得到这个"容器"的大小,也就是这个"容器"里所有元 素的数量,如果这个"容器"里没有任何元素,那么返回0。
  2. int add_item(const string& str)
    add_item 的作用是向"容器"中添加一个字符串元素。如果"容器"中已经存在 内容相同的元素,则添加失败返回-1。如果添加成功,返回这个元素在"容器 "中对应的索引值。
  3. string get_item(int index)
    get_item的作用是取得"容器"中索引为"index"的元素。如果参数"index"大 于(包括等于)当前"容器"中所有元素的数量,或者参数"index"是非正整数, 以上情况都抛出异常"out_of_range"。
  4. bool remove_item(int index)
    remove_item的作用是移除"容器"中索引为"index"的元素。如果参数 "index"大于(包括等于)当前"容器"大小,或者参数"index"是非正整数,返回 “false”;否则删除"容器"中对应索引为"index"的元素,并返回true。
    大家根据提供的功能接口为接口来添加测试用例,再根据测试用例来完成功能的 代码。 ·
    5 参考文献
    http://www.cnblogs.com/coderzh/archive/2009/04/06/1426755.html
    https://code.google.com/p/googletest/w/list
    http://developer.51cto.com/art/201108/285290.htm
    http://www.doc88.com/p-079433664365.html
    Author: 于丽香 yu-lx@neusoft.com
    Date: 2018-09-01 10:28:59 CST
    HTML generated by org-mode 6.33x in emacs 23
 类似资料: