使用gtest也有很长一段时间了,这期间也积累了一些经验,所以分享一下。GTest为我们提供了便捷的测试框架,让我们只需要关注案例本身。如何在GTest框架下写出优美的测试案例,我觉得必须要做到:

  1. 案例的层次结构一定要清晰
  2. 案例的检查点一定要明确
  3. 案例失败时一定要能精确的定位问题
  4. 案例执行结果一定要稳定
  5. 案例执行的时间一定不能太长
  6. 案例一定不能对测试环境造成破坏
  7. 案例一定独立,不能与其他案例有先后关系的依赖
  8. 案例的命名一定清晰,容易理解

案例的可维护性也是非常重要,如果做到上面的8点,自然也就做到了可维护性。下面来分享一下我对于上面8点的经验:

1. 案例的层次结构一定要清晰

所谓层次结构,至少要让人一眼就能分辨出被测代码和测试代码。简单的说,就是知道你在测什么。由于是进行接口测试,我已经习惯了如下的案例层次:

DataDefine

我会将测试案例所需要的数据,以及数据之间的联系全部在预先定义好。测试数据与案例逻辑的分离,有利于维护和扩展测试案例。同时,GTest先天就支持测试数据参数化,为测试数据的分离提供了进一步的便捷。什么是测试数据参数化?就是你可以预先定义好一批各种各样的数据,而你只需要编写一个测试案例的逻辑代码,gtest会将定义好的数据逐个套入测试案例中进行执行。具体的做法请见:玩转Google开源C++单元测试框架Google Test系列(gtest)之四 - 参数化

SUT

SUT,即system under test,表明你的测试对象是什么,它可以是一个类(CUT),对象(OUT),函数(MUT),甚至可以是整个应用程序(AUT)。我单独将这个层次划分出来,主要有两个目的:

  • 明确的表示出你的测试对象是什么
  • 为复杂调用对象包装简单调用接口

明确表示测试对象是什么,便于之后对测试案例的维护和对测试案例的理解。同时,对于一些被测对象,你想要调用它需要经过一系列烦琐的过程,这时,就需要将这一烦琐的调用过程隐藏起来,而只关注被测对象的输入和输出。

TestCase

测试工程中,必须非常明确的表示出哪些是测试案例,哪些是其他的辅助文件。通常,我们会在测试案例的文件名加上Test前缀(或者后缀)。我建议,将所有的测试案例文件或代码放在最显眼的地方,让所有看到你的测试工程的人,第一眼看到的就是测试案例,这很重要。

Checker

对于一个复杂系统的接口测试,仅仅坚持输入和输出是远远不够的。比如测试一个写数据库的函数,函数的返回值告诉你数据已经成功写入是远远不够的,你必须亲身去数据库中查个究竟才行。因此,对于某一类的测试案例,我们可以抽象出一些通用的检查点代码。

如果做到上面的分层,那么一个测试案例写出来的结构应该会是这个样子:

TEST(TestFoo, JustDemo)
{
    GetTestData(); // 获取测试数据
    
    CallSUT(); // 调用被测方法

CheckSomething(); // 检查点验证
}

这样的测试案例,一目了然。

2. 案例的检查点一定要明确

一定要明确案例的检查点是什么,并且让检查点尽量集中。有一个不好的习惯就是核心的检查点在分布在多个函数中,需要不断的跳转才能了解到这个案例检查了些什么。好的做法应该是尽量让检查点集中,能够非常清晰的分辨出案例对被测代码做了哪些检查。所以,尽量让Gtest的ASSERT_和EXPECT_系列的宏放在明显和正确的地方。

3. 案例失败时一定要能精确的定位问题

测试案例失败时,我们通常手忙脚乱。如果一个测试案例Failed,却不能立即推断是被测代码的Bug的话,这个测试案例也有待改进。我们可以在一些复杂的检查点断言中加入一些辅助信息,方便我们定位问题。比如下面这个测试案例:

int n = -1;
bool actualResult = Foo::Dosometing(n);
ASSERT_TRUE(actualResult)

如果测试案例失败了,会得到下面的信息:

Value of: actualResult
Actual: false
Expected:true

这样的结果对于我们来说,几乎没有什么用。因为我们根本不知道actualResult是什么,以及在什么情况下才会出现非预期值。因此,在断言处多加入一些信息,将有助于定位问题:

int n = -1;
bool actualResult = Foo::Dosometing(n);
ASSERT_TRUE(actualResult) << L"Call Foo::Dosometing(n) when n = " << n;

4. 案例执行结果一定要稳定

要保证测试案例在什么时候、什么情况下执行的结果都是一样的。一个一会成功一会失败的案例是没有意义的。要保证案例稳定性的方法有很多,比如杜绝案例之间的影响,有时候,由于前一个案例执行完后,将一些系统的环境破坏了,导致后面的案例执行失败。在测试某些本身就存在一定几率或延时的系统时,使用超时机制是比较简单的办法。比如,你需要测试一个启动Windows服务的方法,如果我们在调用了该方法后立即进行检查,很可能检查点会失败,有时候也许又是通过的。这是因为Windows服务由Stop状态到Running状态,中间还要经过一个Padding状态。所以,简单的做法是使用超时机制,隔断时间检查一次,直到超过某个最大忍受时间。

ASSERT_TRUE(StartService('xxx'));
int tryTimes = 0;
int status = GetServiceStatus('xxx');
while (status != Running)
{
    if (tryTimes >= 10)
        break;
    ::Sleep(200);
    tryTimes++;
    status = GetServiceStatus('xxx');
}
ASSERT_EQ(Running, status) << "Check the status after StartService('xxx')";

5. 案例执行的时间一定不能太长

我们应该尽量让案例能够快速的执行,一方面,我们可以通过优化我们的代码来减少运行时间,比如,减少对重复内容的读取。一方面,对于一些比较耗时的操作,比如文件系统,网络操作,我们可以使用Mock对象来替代真实的对象。使用GMock是一个不错的选择。

6. 案例一定不能对测试环境造成破坏

有的案例需要在特定的环境下来能执行,因此会在案例的初始化时对环境进行一些修改。注意,不管对什么东西进行了修改,一定要保证在案例执行完成的TearDown中将这些环境都还原回来。否则有可能对后面的案例造成影响,或者出现一些莫名其妙的错误。

7. 案例一定独立,不能与其他案例有先后关系的依赖

任何一个案例都不依赖于其他测试案例,任何一个案例的执行结果都不应该影响到别的案例。任何一个案例都可以单独拿出去正确的执行。所以,不能寄希望于前一个案例所做的环境准备,因为这是不对的。

8. 案例的命名一定清晰,容易理解

案例的名字要规范,长不要紧,一定要清晰的表达测试案例的用途。比如,下面的测试案例名称都是不好的:

TEST(TestFoo, Test)
TEST(TestFoo, Normal)
TEST(TestFoo, Alright)

比如像下面的案例名称就会好一点:

TEST(TestFoo, Return_True_When_ParameterN_Larger_Then_Zero)
TEST(TestFoo, Return_False_When_ParameterN_Is_Zero)

编写优美的GTest测试案例相关推荐

  1. Gtest 测试指导 入门基础(A)

    Gtest 测试指导 入门基础(A) Table of Contents • 1 Gtest的基本使用,包括下载,安装,编译. o 1.1 下载 o 1.2 编译  1.2.1 Gtest静态库的编 ...

  2. 测试案例6种编写方法_一种编写测试的好方法

    测试案例6种编写方法 测试. 我最近一直在考虑测试. 作为我对各种项目所做的代码审查的一部分,我已经看到了数千行未经测试的代码. 这不仅是测试覆盖率统计数据指出这一点的情况,更是该项目中根本没有任何测 ...

  3. (转)编写Spring的第一个案例并测试Spring的开发环境

    http://blog.csdn.net/yerenyuan_pku/article/details/52832145 Spring4.2.5的开发环境搭建好了之后,我们来编写Spring的第一个案例 ...

  4. 敏态下“骨架化、模块化”测试案例编写技术实践

    文/叶婷婷 罗章坤 一.引言 随着互联网金融监管的日益严格.市场竞争的不断加剧及客户需求的快速变化,金融企业IT系统的复杂程度不断提高,IT需求日益放大,创新型需求持续产生.传统的软件开发模式,诸如瀑 ...

  5. 用Python编写测试案例

    因为工作需要,需要用Python编写测试案例.接下来会记录在这期间遇到的一些问题和解决方法. Python Faker的使用(1):基础使用方法与函数速查 在软件需求.开发.测试过程中,有时候需要使用 ...

  6. 测试案例编写规范总结

    [适用对象] 所有业务线测试人员,各产品/系统测试负责人. [标准说明] 1.每条测试案例必须只包含一个验证点,不可以一个案例包含多个验证点: 2.每个用户故事(包括业务需求,内部优化需求,紧急变更需 ...

  7. gtest调试_gtest命令行测试案例

    对执行的测试案例进行过滤,支持通配符 ?    单个字符 *    任意字符 -    排除,如,-a 表示除了a :    取或,如,a:b 表示a或b 比如下面的例子: ./foo_test 没有 ...

  8. 项目gtest测试框架 - GoogleTest(十)

    精简版本的C++单元测试框架 ,通过编写这个简单的测试框架,将有助于我们理解gtest. 1. 目录 类型 文件 说明 文件 ./CMakeLists.txt 整体项目工程文件 目录 ./debian ...

  9. gtest 测试部分_全部关于测试–第1部分

    gtest 测试部分 这是三个系列文章中的第一篇. 测试思路 技巧 工具和提示 心态 测试代码是需要学习的东西. 吸收如何做好需要花费时间. 这是一种应该始终练习和改进的技巧. 过去,开发人员没有进行 ...

最新文章

  1. 你未必知道的CSS故事:揭开leading的面纱
  2. 希尔排序c语言,希尔排序(C/C++实现)
  3. 无限极分类不知pid_PHP实现无限极分类
  4. Java和PHP在Web开发方面的比较
  5. bisect git 使用_让 Git Bisect 帮助你
  6. 关于数据库记录排序问题
  7. 【LeetCode】剑指 Offer 42. 连续子数组的最大和
  8. [Hands-on Lab (2) - 使用Helm部署OpenShift应用
  9. 【老孙随笔】想学程序设计,先学人生设计!
  10. ssd测试软件寿命查看,SSD固态硬盘使用寿命检测方法 固态硬盘怎么测剩余寿命?...
  11. CF676A Nicholas and Permutation 题解
  12. python pandas缺失值处理_pandas缺失值的处理
  13. 加一度分享:快手PK抖音,谁更有优势
  14. 如何添加共享计算机用户,局域网共享,教您局域网共享怎么设置
  15. 高企认定人员及研发费要求?
  16. AVPlayerItem的播放时间
  17. 复刻一个羊了个羊掘金商城版
  18. 优秀的海外住宅代理该从哪几个角度判断?
  19. 创维电视android,当贝市场创维酷开专用版
  20. 史上最全的 pom.xml 文件详解

热门文章

  1. 大数据学习(09)--Hadoop2.0介绍
  2. 视频领域的Instagram:Viddy用户突破2600万
  3. JM与h264标准中的关键字说明
  4. 解决 linux 下安装 node 报: command not found
  5. ssm(springMVC + spring+MyBatis) 小例
  6. 利用redis实现分布式锁:加锁与解锁
  7. Tomcat 配置详解/优化方案
  8. LightOJ 1370 Bi-shoe and Phi-shoe(欧拉函数)
  9. 利用光学流跟踪关键点---30
  10. Sublime 的中文乱码问题