原文:Introduction in Java TDD – part 1
翻译:get-set


欢迎来到关于测试驱动开发(TDD)的简介。我们会谈到关于Java和JUnit在TDD中的应用,不过这些都只是工具而已。这篇文章的主要目的是能给您一个关于TDD的深入的理解,而不管什么编程语言和测试框架。

如果你不在你的项目中使用TDD,那么说明你要么懒,要么就是不懂TDD,缺少时间这种理由是站不住脚的。

关于这篇文章

在这篇文章中,我会解释什么是TDD,以及如何在Java中使用;在TDD的什么地方会用到单元测试;在单元测试中你需要做哪些事情;最后,你应该遵循什么原则来编写完美有效的单元测试代码。
如果你已经知道关于Java中的TDD,但是又对例子和教程比较感兴趣,建议你跳过本节直接阅读下一节。

什么是TDD?

如果有人让我简单解释一下什么是TDD,我会说TDD就是在你还没有实现一项功能之前就进行的测试。你可能会质疑:如果还没有实现那如何测试呢?估计肯特·贝克(估计你也猜到了他就是TDD的创始人)会赏你一巴掌。

那么具体如何来做呢?大概有如下几步:
1. 阅读并理解功能需求;
2. 开发一系列的测试来检查这项功能。因为还没有实现功能,因此所有的测试都是红色的;
3. 开发这项功能,直到所有的测试都变成绿色的;
4. 重构代码。

TDD需要的是不同的思路,所以为了基于TDD的理念开始开发,你需要首先忘掉之前的开发思路。这个过程非常痛苦,而且如果你不懂如何编写单元测试的话会更加痛苦,但却是值得的。

基于TDD开发有很大的优势:
1. 你对要实现的功能会有一个更好的理解;
2. 你有明确的“功能是否完成”的指标;
3. 基于测试开发的代码通常之后不需要更多的修改和完善。

得到这一优势的代价也很大——调整到一个新的开发模式的时候的阵痛,以及你在开发每一个新功能的时候可能会花费更多的时间。不过这是时间换质量的代价。

总之这就是TDD的工作原理——编写红色的单元测试,实现相关功能,让所有的测试变绿,重构。

TDD中单元测试的时机

单元测试是自动化测试金字塔中最小的元素,TDD也是基于此。单元测试可以帮助我们检查业务逻辑,编写单元测试代码也很容易。那么你如何使用单元测试呢?我会尽量通过简单的方式解释给你。

单元测试应该越小越好。但是不要认为一个方法对应一个测试,不过也有可能存在这种情况。真正的规则在于一个单元测试对应一组多个方法的调用,这叫做“基于行为的测试”(testing of behaviour)。
我们来看一下Account这个类:

public class Account {private String id = RandomStringUtils.randomAlphanumeric(6);private boolean status;private String zone;private BigDecimal amount;public Account() {status = true;zone = Zone.ZONE_1.name();amount = createBigDecimal(0.00);}public Account(boolean status, Zone zone, double amount) {this.status = status;this.zone = zone.name();this.amount = createBigDecimal(amount);}public enum Zone {ZONE_1, ZONE_2, ZONE_3}public static BigDecimal createBigDecimal(double total) {return new BigDecimal(total).setScale(2, BigDecimal.ROUND_HALF_UP);}@Overridepublic String toString() {StringBuilder sb = new StringBuilder();sb.append("id: ").append(getId()).append("\nstatus: ").append(getStatus()).append("\nzone: ").append(getZone()).append("\namount: ").append(getAmount());return sb.toString();}public String getId() {return id;}public boolean getStatus() {return status;}public void setStatus(boolean status) {this.status = status;}public String getZone() {return zone;}public void setZone(String zone) {this.zone = zone;}public BigDecimal getAmount() {return amount;}public void setAmount(BigDecimal amount) {if (amount.signum() < 0)throw new IllegalArgumentException("The amount does not accept negative values");this.amount = amount;}
}

注意,这个类中有4个getter方法。如果我们为每个getter方法创建一个单独的单元测试,代码就会有很多冗余行。这种情况下基于行为的测试就比较有用了。试想我们现在需要测试某一个构造器在创建对象时是否正确,如何检查创建的对象是否如预期呢?我们需要检查每一个属性的值,getter方法就可以用于这样的场景。

创建小而精的单元测试,因为它们需要在每次提交到git或编译之前都应该执行。为了理解单元测试的速度的重要性,试想一个项目有1000个单元测试,每个测试花费100ms,那么跑完所有的测试将花费1分40秒的时间。
实际上对于单元测试来说,100ms的时间太长了,所以你应当通过采用不同的规则和技术来降低测试时间,比如,不要在单元测试中进行数据库连接的测试,也不要在@Before注解的地方运行大型对象的初始化。

要给单元测试起好名字。测试的名字可以很长,主要是它能够表达清楚测试什么。例如我需要测试Account类的一个构造方法,我会命名为defaultConstructorTest。另一方面,建议在命名之前先把测试逻辑写出来,这样可能会更容易命名。

单元测试应该是可预测的。这是最明显不过的了。例如,为了检查转账(5%手续费)操作,你需要知道转账的金额和作为输出的到账金额。比如转账100块而收到95块。

最后,单元测试结果是容易获得的。当你给每一个业务逻辑场景都构建一个测试的时候,可以得到很多测试结果反馈,从而能够精确定位到测试失败的场景。

所有的这些建议目的在于提高单元测试的设计水平,但是还有一点——基本的测试设计原理。

基本的测试设计原理

如果没有测试数据,那么测试也无从谈起。例如,当你测试转账业务逻辑时,你需要设置一些数字作为转账发起金额。这些金额就是测试数据。那么问题来了,你应该如何选择测试数据呢?为了回答这个问题,我们需要过一遍最主流的测试设计原理。其主要目的就是构建测试数据。

首先,我们假设转账金额只能为正整数,另外上限是1000元。那么可以表示为:

0 < amount <= 1000; amount in integer

我们所有的测试场景可以被分为两组:正确 & 错误。第一组测试数据是允许的,应该得到正确的结果;第二组就是错误场景。

依据同类数据原理(classes of equivalence technic),我们随机选取(0, 1000]范围内的数字进行测试,比如500,如果500成功的话,我们认为所有的位于该范围内的数字也是成功的。我们也可以选择区域内的其他类型数据,比如浮点型数字123.45。

然后我们依据边界测试原理(boundary testing technic),选择2个有效值,分别位于数字范围的两头,这里我们选择1作为最小值,1000作为最大值。

下一步就是选择2个无效值,比如0和1001.

最终我们有6个测试数据:
(1, 500, 1000)——作为正确场景
(0, 125.50, 1001)——作为错误场景

总结

在这篇文章中,我解释了TDD以及单元测试在TDD中的重要性。我希望在如此啰嗦的解释后,大家能够在实际的开发过程中进行应用。下一节我将会说明如何在功能代码开发前编写测试代码。我们会一步一步来,以一个文件分析的例子开始,直到最终完成代码重构。

一定让所有的测试都变成绿色的哟 : )

Java TDD介绍-1相关推荐

  1. 流行的9个Java框架介绍: 优点、缺点等等

    流行的9个Java框架介绍: 优点.缺点等等 在 2018年,Java仍然是世界上最流行的编程语言.它拥有一个巨大的生态系统,在全世界有超过900万Java开发人员.虽然Java不是最直接的语言,但是 ...

  2. Android下HelloWorld项目的R.java文件介绍

    R.java文件介绍 HelloWorld工程中的R.java文件 package com.android.hellworld; public final class R {     public s ...

  3. java英语介绍_java,英文介绍项目.doc

    java,英文介绍项目 java,英文介绍项目 篇一:Java开发常见英文 Java基础常见英语词汇(共70个) Author:ZW OO: object-oriented ,面向对象OOP: obj ...

  4. Java命令学习系列(零)——常见命令及Java Dump介绍

    Java命令学习系列(零)--常见命令及Java Dump介绍 一.常用命令: 在JDK的bin目彔下,包含了java命令及其他实用工具.  jps:查看本机的Java中进程信息.  jstack ...

  5. java coin介绍_代码示例中的Java 7:Project Coin

    java coin介绍 该博客通过代码示例介绍了一些新的Java 7功能,这些项目在Project Coin一词下进行了概述. Project Coin的目标是向JDK 7添加一组小的语言更改.这些更 ...

  6. java hadoop api_Hadoop 系列HDFS的Java API( Java API介绍)

    HDFS的Java API Java API介绍 将详细介绍HDFS Java API,一下节再演示更多应用. Java API 官网 如上图所示,Java API页面分为了三部分,左上角是包(Pac ...

  7. js 中转换成list集合_程序员:java集合介绍-List,具说很详细,你不来看看?

    Java集合介绍 作为一个程序猿,Java集合类可以说是我们在工作中运用最多.最频繁的类.相比于数组(Array)来说,集合类的长度可变,更加方便开发. Java集合就像一个容器,可以存储任何类型的数 ...

  8. protobuf 3.5 java使用介绍(二)

    protobuf 3.5 java使用介绍(二) 上一篇遗留了两个问题: 1,数据模型中有可能会出现数组格式,而数组里面是一个其他的模型,这个怎么来做? 2,构建数据消息的时候,通常会有一个头,一个体 ...

  9. java自我介绍_口语化java自我介绍

    口语化java自我介绍 自我介绍是对个人特点的总结与归纳.一个好的自我介绍可以让HR很快地了解自己,欣赏自己.下面小编为大家整理了口语化java自我介绍,希望大家喜欢. 口语化java自我介绍(一) ...

最新文章

  1. 应用层下的人脸识别(一):图像获取
  2. linux 查看libevent 安装目录,linux下libevent安装配置与简介 以及 linux库文件搜索路径的配置...
  3. 强人总结的哄老婆秘籍
  4. 以太坊(Ethereum ETH)是如何计算难度的
  5. Spring Boot集成CKEditor
  6. html 页面友情提示,HTML参考
  7. 【线上直播】深度学习简介与落地实战经验分享
  8. kubernetes1.8.4 安装指南 -- 8. 安装Kube DNS
  9. 【征稿倒计时—山东科技大学主办】 2021智能装备与特种机器人国际会议(ICIESR2021)...
  10. python3 规则引擎_几个常见规则引擎的简单介绍和演示
  11. 是人是谁_谁是白鹤滩最可爱的人
  12. idea 启动选择profiles_玩转SpringBoot 2 之项目启动篇
  13. 阿里健康App更名为“医鹿”,加入阿里动物园式命名
  14. 计算机ppt试题训练,powerpoint2010操作题 计算机POWERPOINT操作题
  15. python购物信息整合教程_python实现简单购物商城
  16. swift学习之元组
  17. 大熊君JavaScript插件化开发------(实战篇之DXJ UI ------ ItemSelector)
  18. CSS 绘制一个时钟
  19. CSU 2124智慧树(建图+BFS)
  20. MessageDigest 类的用法

热门文章

  1. APP运营推广,如何做到“饱和攻击”
  2. HDU 6194 string string string
  3. 表横竖转换(行列转换)PIVOT 和 UNPIVOT 用法
  4. amap 高德 地图打点 地图描点 描点居中 清除打点
  5. 交互设计师必须知道的五大交互设计流程
  6. 全卷积网络FCN与卷积神经网络CNN的区别
  7. Protect访问权限
  8. IntelliJ IDEA 如何配置MySQL数据库
  9. 粮仓分布式多点测温的简单参考方案
  10. Unity入门常见错误之碰撞检测方法无法触发