优秀的Java程序测试是什么样的?

作为测试驱动设计和开发的忠实粉丝,我相信创造良好的测试是我们作为Java开发人员可以做的最重要的事情之一。我们写测试出于许多原因:

  • 塑造系统的设计。我们知道输入和输出应该是什么样的,但是我们需要创建什么对象来做到这一点呢?代码应该塑造成什么样的“形状”?编写测试可以让我们知道应该创建什么样的代码。
  • 为了确保初始和持续的正确性。让我们的应用程序如期望地那样运作并且始终如一地精确很重要。测试应该竭力确保做到这一点。
  • 文档。测试是系统的文档,因为它会说明它应该做什么以及应该怎么做。

那么“好的测试”到底是什么样子的呢?

给测试命名

测试的名字至关重要,特别是从文档角度来看的话。我们应该能够大声读出测试的名字就像一组需求一样。事实上,有一个伟大的IntelliJ插件,叫Enso,它会将你的测试名转变为恰好位于每个类旁边的语句,这样你就可以明明白白地看到你在做什么。

不要以“test”开始命名测试的名称。这是来自于JUnit初期的后遗症,当需要它执行的时候。你的Test类将在Test文件夹中,在一个最后有Test这个单词的类中。会有一个@Test的注解。我们知道这是一个测试。

你也应该避免以“should”或“will”开头。这些都是干扰词。既然你已经为这个功能写了一个测试,那我们就知道它“should或will”工作(如果不能工作的话,那我们知道我们需要修复它)。

将测试名称当作一个要求。 下面是一些例子

不要害怕表达出来。如果你的测试名称确实需要很长的一串单词,那就这么做,只要它能清楚说明将发生什么事情。

测试代码

测试将分为3个部分:设置,操作,断言。

设置

对你的测试设置代码应该只与在测试中被断言的值相关。如果你有多余的设置代码,那就会搞不清楚它是什么,并且与测试不相关。

这可以通过多种方式实现:

  • 将通用设置移动到使用@Before注解的具体设置方法。
  • 将重复的设置代码移动到辅助方法
  • 使用Maker来创建复杂的测试对象,并只设置测试中相关的值。

我重申一下:每个测试的设置部分应该只有与最后被断言的值相关的代码。

不好的例子:

书店的初始化发生在测试中,书本的创建也是。这让测试显得混乱不堪,让人搞不清楚发生了什么事情。

好的例子:

初始化发生在字段中,这样在测试中发生了什么一清二楚。

操作

小菜一碟!最好保持到一行,你要进行测试的独立操作。有时候,你专门测试的是输出是什么,如果某些东西被多次调用,或者在某些优先操作之后调用的结果是什么,所以这不是一个硬性规定。当读取测试时,用户应该快速而轻松地能说“将这些值设置成这样,如果我执行这个操作/这些操作,那么这是预期的结果”。在上面的例子中,便是bookstore.findByTitle()方法。

断言

使用Hamcrest。 Hamcrest是一个很棒的库,给我们一个流畅的API用来写入测试。不会像这样的代码:

我们可以一目了然、轻松地阅读像这样的代码:

这些相当简单的例子:Hamcrest有很多伟大的方法,使编写复杂测试变得很容易,并允许你创建自己的匹配器。

当然,理想情况下,我们希望有一个独立的断言。这可以让我们知道我们正在测试什么,并说明我们的代码没有意外情况。就像这篇文章中所说的那样,这不是一个硬性的规则,因为在某些情况下,这是必要的,但如果你有这样一个的测试:

那么要理解测试哪里失败或哪条断言重要就变得困难多了。

你也可以在Hamcrest中编写自定义的匹配器,因为Hamcrest可为复杂断言提供一个优雅的解决方案。如果你需要在一个循环中运行断言,或者你有大量的字段要断言,那么一个自定义的匹配器可能才是上上之选。

一个测试的最重要的部分之一是,当它失败时,哪怕是一个5岁孩子也应该看得出什么地方出了错以及哪里错了。失败的消息一定不能含糊。关于这方面的解决方法是:

  • 如果做任何类型的对象比较,那么保证对象有一个体面的toString()消息。没有什么比<MyObject @ 142131>不匹配更糟的了。
  • 想要做的更好的话,可以对你的对象使用自定义匹配器。你可以准确地知道哪些字段未能匹配。
  • 确保明确为什么你要选择和这个值作比较。例如,如果你正在将一个字段值与数字3000比较,那么为什么是3000?你应该费力地明白这一点。显然,这个数字不是随便得来的,并且还要确保该变量的命名可以显示它的值是如何得来的。

所有这些都应该是在一个适度的常识范围内。没有严格规定。

原文发布于微信公众号 - java一日一条(mjx_java)

原文发表时间:2017-01-13

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java帮帮-微信公众号-技术文章全总结

面试复习大纲(最全面)

Java基础 1.数组中的排序问题(笔试或者机试,前者可能性更大) 2.面向对象的理解 面向对象主要有四个特性: 封装、抽象、继承和多态。 封装:在面向对象语言...

3525
来自专栏FreeBuf

根据目标用户信息,Python生成WPA2密码字典

如何根据目标WiFi的用户信息,用Python生成一份独特的WPA2密码表? 随着无线网络的不断发展,几乎所有场合都会覆盖WIFI信号,无论是公共地点还是家庭之...

4015
来自专栏编程一生

mock打桩之EasyMock

2361
来自专栏小狼的世界

Go学习入门

Go语言的历史要从贝尔实验室开始说起,我们知道贝尔实验室计算科学研究中心的肯•汤普逊 Ken Thompson 和丹尼斯 • 里奇 Dennis Ritchie...

3382
来自专栏PHP在线

MySQL时间函数,用起来比PHP还爽

前一篇写了PHP的时间函数(还是草稿),这一篇就写Mysql的时间函数吧。最近做的项目,关乎权限,于是自然而然的就与有效期联系在了一起。其 中有一个功能是生成特...

3416
来自专栏Java架构

最全的BAT大型互联网公司面试题整理

最近有很多网友都在求大厂面试题。正好我之前电脑里面有这方面的整理,于是就发上来分享给大家。

1.4K4
来自专栏java一日一条

优秀的Java程序测试是什么样的?

测试的名字至关重要,特别是从文档角度来看的话。我们应该能够大声读出测试的名字就像一组需求一样。事实上,有一个伟大的IntelliJ插件,叫Enso,它会将你的测...

642
来自专栏西枫里博客

thinkPHP升级到5.0.13导致update更新出错的问题

话题还得从tinkPHP群消息说起,双十二那天,群内通知官方发布了5.0.13版本。刚好那几天在折腾redis缓存。官方文档中说5.0.13是一个集合诸多改进的...

1085
来自专栏杨建荣的学习笔记

初识YAML

晚上本来想看看Julia语言的,最后发现需要花额外的不少时间,就先放放,那就看看Yaml吧,要学习的话,周期短,本身也比较简单清晰。 早些年的...

2837
来自专栏GIS讲堂

leaflet加载天地图

leaflet是一个轻量级的并且开源的地图框架,是由esri发起的,由于其轻量、简单而被大家喜欢,本文带你学习如何在leaflet中加载天地图。

6353

扫码关注云+社区

领取腾讯云代金券