欢迎来到测试驱动开发 (TDD)系列的介绍。 我们将在TDD上下文中讨论Java和JUnit ,但这只是工具。 本文的主要目的是使您全面了解TDD,而无论使用哪种编程语言和测试框架。
如果您在项目中不使用TDD,那么您要么很懒,要么就是根本不知道TDD的工作方式。 关于缺乏时间的借口不适用于这里。
关于这篇文章
在这篇文章中,我将解释什么是TDD以及如何在Java中使用它。 在TDD中,单元测试应放在哪个位置。 单元测试必须涵盖的内容。 最后,为了编写良好而有效的单元测试,需要遵循哪些原则。
如果您已经了解Java中有关TDD的所有知识,但是对示例和教程感兴趣,那么我建议您跳过这一部分,继续下一部分(它将在这一部分发布后的一周内发布)。
什么是TDD?
如果有人要我用几句话来解释TDD,那么我说TDD是功能实现之前的测试开发。 您可以争辩:很难测试尚不存在的事物。 肯特·贝克(Kent Beck)可能会为此一巴掌。
那怎么可能呢? 可以通过以下步骤进行描述:
1.阅读并了解特定功能的要求。
2.您开发了一组测试功能的测试。 由于没有功能实施,所有测试均为红色。
3.开发功能,直到所有测试变为绿色。 4.重构代码。
TDD需要不同的思维方式,因此,为了根据它开始工作,您需要忘记以前开发代码的方式。 这个过程很难。 如果您不知道如何编写单元测试,那就更难了。 但这是值得的。
使用TDD进行开发具有宝贵的优势:
1.您对实现的功能有更好的了解。
2.您具有功能完整性的可靠指标。
3.代码包含测试,并且被修复或新功能破坏的可能性较小。
这些优势的代价是很高的–与切换到新的开发方式有关的不便以及您花费在开发每个新功能上的时间。 这是质量的代价。
这就是TDD的工作方式–编写红色的单元测试,开始实现功能,使测试变为绿色,执行代码重构。
TDD中单元测试的位置
由于单元测试是测试自动化金字塔中最小的元素,因此TDD基于它们。 借助单元测试,我们可以检查任何类的业务逻辑。 如果您知道如何做,则编写单元测试很容易。 那么,您需要使用单元测试进行测试的内容以及该怎么做呢? 您知道这些问题的答案吗? 我将尝试以简洁的形式说明答案。
单元测试应尽可能小。 不,不不要考虑这一点,因为一项测试仅适用于一种方法。 当然,这种情况也是可能的。 但通常,一个单元测试意味着调用多种方法。 这称为行为测试。
让我们考虑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方法创建一个单独的单元测试,则会得到太多的冗余代码行。 可以通过行为测试来处理这种情况。 想象一下,我们需要使用其构造函数之一来测试对象创建的正确性。 如何检查对象是否按预期创建? 我们需要检查每个字段的值。 因此,可以在这种情况下使用吸气剂。
创建小型快速的单元测试 ,因为它们应在每次提交到git存储库并将新的构建提交到服务器之前执行。 您可以考虑一个带有实数的示例,以了解单元测试速度的重要性。 假设一个项目有1000个单元测试。 他们每个人都需要100毫秒。 结果,所有测试的运行需要1分40秒。
实际上100ms对于单元测试来说太长了,因此您必须通过应用不同的规则和技术来减少运行时间,例如,不要在单元测试中执行数据库连接(根据定义,单元测试是隔离的)或在其中执行昂贵的对象的初始化。 @Before块。
为单元测试选择好名字 。 测试的名称可以是您想要的任何名称,但是它应该代表测试进行的验证。 例如,如果我需要测试Account类的默认构造函数,则将其命名为defaultConstructorTest 。 选择测试名称的另一个有用建议是在命名测试之前编写测试逻辑。 在开发测试时,您会了解其中发生的情况,因此名称的组成变得更加容易。
单元测试应该是可预测的 。 这是最明显的要求。 我将在示例中进行解释。 为了检查转帐操作(需支付5%的费用),您必须知道发送的金额以及输出的金额。 此测试方案可以实现为发送100美元和接收95美元。
最后应该对单元测试进行细化 。 在每个测试中放置一个逻辑场景时,您可以从测试中获得有用的反馈。 并且在发生单个故障的情况下,您不会丢失有关其余功能的信息。
所有这些建议旨在改进单元测试设计。 但是,您还需要了解一件事-测试设计技术的基础知识。
测试设计技术基础
没有测试数据就不可能编写测试。 例如,当您测试汇款系统时,可以在汇款字段中设置一些金额。 在这种情况下,数量是测试数据。 那么您应该选择哪些值进行测试? 为了回答这个问题,我们需要经历最流行的测试设计技术。 测试设计技术的通用目的是简化测试数据的构成。
首先,让我们假设我们可以发送正整数的钱。 同样,我们发送的邮件不能超过1000。这可以表示为:
0 < amount <= 1000; amount in integer
我们所有的测试场景可以分为两组:正面和负面场景。 第一个用于系统允许的测试数据,并导致成功的结果。 第二种是所谓的“故障场景”,当我们使用不适当的数据与系统进行交互时。
根据等价技术的类别,我们可以从(0; 1000]范围内选择一个随机整数。设为500。由于系统适用于500,因此对于该范围内的所有整数均适用。有效值,也可以从范围中选择无效的输入,可以是任何带浮点的数字,例如125.50
然后我们必须参考边界测试技术 。 根据它,我们必须从范围的左侧和右侧选择2个有效值。 在我们的例子中,我们以1为允许的最小正整数,从右边取1000。
下一步是在边界上选择2个无效值。 所以它是0和1001
因此,最后我们有6个值需要在单元测试中使用:
- (1,500,1000)–对于积极的情况
- (0,125.50,1001)–否定情况
摘要
在这篇文章中,我试图解释TDD的各个方面,并说明在TDD中单元测试的重要性。 因此,我希望经过如此详尽和长期的bla-bla理论,我们可以继续实践。 在下一篇文章中,我将演示如何在功能之前开发测试。 我们将从文档分析开始,到代码重构结束,逐步进行操作。
确保所有测试都是绿色的:)
翻译自: https://www.javacodegeeks.com/2015/11/introduction-in-java-tdd-part-1.html