视频参考:Google C++ Testing GTest GMock Framework

为什么要使用 Google C++ Testing Framework?

使用这个框架有许多好理由。本文讨论其中几个。

某些类型的测试有糟糕的内存问题,这些问题只在某几次运行期间出现。Google 的测试框架为处理这种情况提供了出色的支持。可以使用 Google 框架重复运行相同的测试一千次。当出现故障的迹象时,自动地调用调试器。另外,这只需要在命令行上传递两个开关即可实现:--gtest_repeat=1000 --gtest_break_on_failure

与其他许多测试框架相反,可以把 Google 测试框架内置的断言部署在禁用了异常处理(通常由于性能原因)的软件中。因此,也可以在析构函数中安全地使用断言。

运行测试很简单。只需调用预定义的 RUN_ALL_TESTS 宏,而不需要通过创建或驱动单独的运行器类来执行测试。这比 CppUnit 等框架方便多了。

只需传递一个开关即可生成 Extensible Markup Language (XML) 报告: --gtest_output="xml:<file name>"。在 CppUnit 和 CppTest 等框架中,需要编写很多代码才能生成 XML 输出。

创建基本测试

以下代码均在Linux下运行。也可以利用VS2017 cross platform feature 在Windows本地进行Gtest测试。

sample.h

#ifndef _SAMPLE_H_
#define _SAMPLE_H_ // Returns n! (the factorial of n). For negative n, n! is defined to be 1.
int Factorial(int n); // Returns true iff n is a prime number.
bool IsPrime(int n); #endif

sample.c

#include "sample.h"

// Returns n! (the factorial of n).  For negative n, n! is defined to be 1.
int Factorial(int n) {
int result = ;
for (int i = ; i <= n; i++) {
result *= i;
} return result;
} // Returns true iff n is a prime number.
bool IsPrime(int n) {
// Trivial case 1: small numbers
if (n <= ) return false; // Trivial case 2: even numbers
if (n % == ) return n == ; // Now, we have that n is odd and n >= 3. // Try to divide n by every odd number i, starting from 3
for (int i = ; ; i += ) {
// We only have to try i up to the square root of n
if (i > n/i) break; // Now, we have i <= n/i < n.
// If n is divisible by i, n is not prime.
if (n % i == ) return false;
} // n has no integer factor in the range (1, n), and thus is prime.
return true;
}

sample_unittest.c

#include <limits.h>
#include "sample.h"
#include "gtest/gtest.h"
namespace { TEST(FactorialTest, Negative) {
// This test is named "Negative", and belongs to the "FactorialTest"
// test case.
EXPECT_EQ(, Factorial(-));
EXPECT_EQ(, Factorial(-));
EXPECT_GT(Factorial(-), );
} TEST(FactorialTest, Zero) {
EXPECT_EQ(, Factorial());
} TEST(FactorialTest, Positive) {
EXPECT_EQ(, Factorial());
EXPECT_EQ(, Factorial());
EXPECT_EQ(, Factorial());
EXPECT_EQ(, Factorial());
} // Tests IsPrime()
TEST(IsPrimeTest, Negative) {
EXPECT_FALSE(IsPrime(-));
EXPECT_FALSE(IsPrime(-));
EXPECT_FALSE(IsPrime(INT_MIN));
} TEST(IsPrimeTest, Trivial) {
EXPECT_FALSE(IsPrime());
EXPECT_FALSE(IsPrime());
EXPECT_TRUE(IsPrime());
EXPECT_TRUE(IsPrime());
} TEST(IsPrimeTest, Positive) {
EXPECT_FALSE(IsPrime());
EXPECT_TRUE(IsPrime());
EXPECT_FALSE(IsPrime());
EXPECT_TRUE(IsPrime());
}
} // namespace

g++ sample.c sample_unittest.c  -lgtest -std=c++11 -lgtest_main -lpthread -o test

没有main函数

使用Gtest你可以不提供main函数,libgtest_main.a会为你提供一个。如果没有特殊理由还是建议自己提供main函数

main.c

#include <gtest/gtest.h>
int main(int argc, char** argv){
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

g++ sample.c sample_unittest.c  main.c -lgtest -std=c++11 -lpthread -o test

::testing::InitGoogleTest 方法的作用就是对框架进行初始化,必须在调用 RUN_ALL_TESTS 之前调用它。在代码中只能调用 RUN_ALL_TESTS 一次,因为多次调用会与框架的一些高级特性冲突,不支持这种做法。注意,RUN_ALL_TESTS自动地探测并运行用 TEST 宏定义的所有测试。在默认情况下,结果输出到标准输出。

编译、运行结果和上面一样。

Gtest选项

InitGoogleTest 函数接收传递给test infrastructure的参数,下面介绍常用参数

通过在命令行上传递 --gtest_output="xml:report.xml",可以把输出转储为 XML 格式。当然,可以把 report.xml 替换为您喜欢的任何文件名。

某些测试有时候会失败,但是在大多数时候会顺利通过。这是与memory corruption相关的问题的典型特点。如果多次运行测试,就能够提高发现失败的可能性。如果在命令行上传递 --gtest_repeat=2 --gtest_break_on_failure,就重复运行相同的测试两次。如果测试失败,会自动调用调试器。

并不需要每次都运行所有测试,尤其是在修改的代码只影响某几个模块的情况下。为了支持运行一部分测试,Google 提供 --gtest_filter=<test string>。test string 的格式是由冒号 (:) 分隔的一系列通配符模式。例如,--gtest_filter=* 运行所有测试,而 --gtest_filter=SquareRoot* 只运行 SquareRootTest 测试。如果希望只运行 SquareRootTest 中的正数单元测试,应该使用 --gtest_filter=SquareRootTest.*-SquareRootTest.Zero*。注意,SquareRootTest.* 表示属于 SquareRootTest 的所有测试,而 -SquareRootTest.Zero* 表示不运行名称以 Zero 开头的测试。

禁用临时测试

可以临时禁用测试吗?可以,只需在逻辑测试名或单元测试名前面加上 DISABLE_ 前缀,它就不会执行了。

禁用临时测试

#include "gtest/gtest.h"

TEST (DISABLE_SquareRootTest, PositiveNos) {
EXPECT_EQ (18.0, square-root (324.0));
EXPECT_EQ (25.4, square-root (645.16));
EXPECT_EQ (50.3321, square-root (2533.310224));
} OR TEST (SquareRootTest, DISABLE_PositiveNos) {
EXPECT_EQ (18.0, square-root (324.0));
EXPECT_EQ (25.4, square-root (645.16));
EXPECT_EQ (50.3321, square-root (2533.310224));
}

注意,如果禁用了任何测试,Google 框架会在测试执行结束时输出警告消息,Google 警告用户在框架中有禁用的测试

 FAILED TEST
YOU HAVE DISABLED TEST

如果希望继续运行禁用的测试,那么在命令行上传递 -gtest_also_run_disabled_tests 选项。

Assertions(断言)

演示用于浮点数比较的宏

ASSERT_FLOAT_EQ (expected, actual)
ASSERT_DOUBLE_EQ (expected, actual)
ASSERT_NEAR (expected, actual, absolute_range) EXPECT_FLOAT_EQ (expected, actual)
EXPECT_DOUBLE_EQ (expected, actual)
EXPECT_NEAR (expected, actual, absolute_range)

为什么需要用单独的宏进行浮点数比较?使用 ASSERT_EQ 不行吗?使用 ASSERT_EQ 和相关的宏可能可以,也可能不行,但是使用专门用于浮点数比较的宏更好。通常,不同的中央处理单元 (CPU) 和操作环境以不同的方式存储浮点数,简单地比较期望值和实际值是无效的。例如,ASSERT_FLOAT_EQ (2.00001, 2.000011) 会顺利通过 — 如果直到小数点后四位都匹配,Google 就不会抛出错误。如果需要更精确的比较,应该使用 ASSERT_NEAR (2.00001, 2.000011, 0.0000001),就会得到 下面所示的错误。

Math.cc(): error: The difference between 2.00001 and 2.000011 is 1e-, which exceeds
0.0000001, where
2.00001 evaluates to 2.00001,
2.000011 evaluates to 2.00001, and
0.0000001 evaluates to 1e-.

断言引发的三种结果

Assertions会引发3种结果:success、Non-Fatal Failure、Fatal Failure

Non-Fatal Failure 和 Fatal Failure啥区别?

前者失败后还会继续执行,后者失败后停止执行。ASSERT_XX属于fatal assertion,EXPECT_XX属于nonfatal assertion。

不建议才一个测试单元里面写多个assertion

当有多个Non-Fatal Assertion时,不管有多少个assertion通过,只要有一个不通过,该测试用例就不通过。

如果把第一个EXPECT_EQ换成ASSERT_EQ,那么断言失败时停止执行,后面代码不会被执行。虽然你可以通过日志去翻那个文件、哪个函数、哪段代码第几行执行错误。这种情况适合于测试用例少的情况,上百个测试用例的时候,这种排查发简直是噩梦。一个建议原则是one region one assertion

理解test fixtures

在执行单元测试之前,通常要执行一些定制的初始化。例如,如果希望度量测试的时间/内存占用量,就需要放置一些测试专用代码以度量这些值。这就是fixtures的用途 — 它们帮助完成这种定制的测试初始化。代码如下

A test fixture class
class myTestFixture1: public ::testing::test {
public:
myTestFixture1( ) {
// initialization code here
} void SetUp( ) {
// code here will execute just before the test ensues
} void TearDown( ) {
// code here will be called just after the test completes
// ok to through exceptions from here if need be
} ~myTestFixture1( ) {
// cleanup any pending stuff, but no exceptions allowed
} // put in any custom data members that you need
};

这个fixtures class派生自 gtest.h 中声明的 ::testing::test 类。下面是使用这个装备类的示例。注意,它使用 TEST_F 宏而不是 TEST

TEST_F (myTestFixture1, UnitTest1) { 

.
} TEST_F (myTestFixture1, UnitTest2) { .
}

在使用装备时,要注意以下几点:

  • 可以在构造函数或 SetUp 方法中执行初始化或分配资源。由用户选择具体方式。
  • 可以在 TearDown 或析构函数例程中释放资源。但是,如果需要异常处理,那么只能在 TearDown 代码中进行,因为从析构函数中抛出异常会导致不确定的结果。
  • 在以后的版本中,Google 断言宏可能会在平台上抛出异常。因此,为了便于维护,最好在 TearDown 代码中使用断言宏(assertion macros)。
  • 不存在多个测试使用同一个test fixture。对于每个新的测试单元,框架创建一个新的test fixture。在上面代码中,由于要创建两个 myFixture1 对象,所以两次调用 SetUp 例程(请注意使用正确的拼写)。

A quick introduction to Google test的更多相关文章

  1. A Quick Introduction to Linux Policy Routing

    A Quick Introduction to Linux Policy Routing 29 May 2013 In this post, I’m going to introduce you to ...

  2. Quick Introduction to SQL Server Profiler

    Introduction to Profiler SQL Server Profiler — or just Profiler — is a tool that can help monitor al ...

  3. A quick introduction to HTML

    w3c reference : https://www.w3.org/TR/2014/REC-html5-20141028/introduction.html#writing-secure-appli ...

  4. an introduction of google breakPad for android

    一.背景 众所周知,Android JNI层的Crash问题是个比较头疼的问题.相对Java层来说,由于c/c++造成的crash没有输出如同 Java的Exception Strace,所以cras ...

  5. A quick introduction to Source Insight for seamless development platform between Linux and Windows

    前言 Source Insight是一个面向项目开发的程序编辑器和代码浏览器,它拥有内置的对C/C++, C#和Java等程序的分析.能分析源代码并在工作的同时动态维护它自己的符号数据库,并自动显示有 ...

  6. Google Test资料

    Google Test资料 玩转Google开源C++单元测试框架Google Test系列(gtest)(总) gtest.h file not found googletest xcode 7.0 ...

  7. 转载:Practical UML™: A Hands-On Introduction for Developers

    原文:http://edn.embarcadero.com/article/31863 By: Randy Miller Abstract: This tutorial provides a quic ...

  8. Google 地图 API for Android

    原文:Introduction to Google Maps API for Android 作者:Eunice Obugyei 译者:kmyhy 从健康类 app Runkeeper 到游戏 app ...

  9. Google C++测试框架系列入门篇:第一章 介绍:为什么使用GTest?

    原始链接:Introduction: Why Google C++ Testing Framework? 词汇表 版本号:v_0.1 介绍:为什么使用GTest? GTest帮助你写更好的C++测试代 ...

随机推荐

  1. Appium之Toast元素识别

    问题思考 在日常使用App过程中,经常会看到App界面有一些弹窗提示(如下图所示)这些提示元素出现后等待3秒左右就会自动消失,那么我们该如何获取这些元素文字内容呢? Toast简介 Android中的 ...

  2. Python的Colorama模块

    简介 Python的Colorama模块,可以跨多终端,显示字体不同的颜色和背景,只需要导入colorama模块即可,不用再每次都像linux一样指定颜色. 1. 安装colorama模块 1 pip ...

  3. 【ARM-Linux开发】ARM嵌入式设备Linux系统启动步骤和方式

    1). 简介 本文简单介绍ARM嵌入式设备基于嵌入式Linux操作系统时候的启动步骤和启动方式, 区别与X86平台,ARM平台下并没有一个标准的启动步骤,不同ARM SoC都会使用各自定义的boot ...

  4. redis 简单教程

    一.redis的安装 安装环境:centos 7 1) 下载redis 这里我们下载的是redis-4.0.10.tar.gz 2)将redis tar包移动至 /usr/local 执行如下命令 c ...

  5. 用js实现call方法

    Function.prototype.call2 = function (context, ...args) {   var context = context || window;   //改变th ...

  6. 浏览器解析js和type判断数据类型

    ### 浏览器解析: - 1.当浏览器(内核.引擎)解析和渲染js的时候,会给js提供一个运行的环境,这个环境叫做“全局作用域(后端global / 客服端window scope)” - 2.代码自 ...

  7. .NET Core 之 Nancy 基本使用

    Nancy简介 Nancy是一个轻量级的独立的框架,下面是官网的一些介绍: Nancy 是一个轻量级用于构建基于 HTTP 的 Web 服务,基于 .NET 和 Mono 平台,框架的目标是保持尽可能 ...

  8. 【LOJ2292】[THUSC2016]成绩单(区间DP)

    题目 LOJ2292 分析 比较神奇的一个区间 DP ,我看了很多题解都没看懂,大约是我比较菜罢. 先明确一下题意:abcde 取完 c 后变成 abde ,可以取 bd 这样取 c 后新增的连续段. ...

  9. java中参数" ..."的用法和意思

    public static void executebindParam(PreparedStatement pstmt,Object ...os){ int len = os.length; try ...

  10. Linux下将.Asp Core 部署到 Docker容器中

    我们来部署一个简单的例子: 将一个简单的.Aps Core项目部署到Docker容器中并被外网访问 说明: 下面的步骤都是建立在宿主服务器系统已经安装配置过Docker容器,安装Docker相对比较简 ...