仅测试 public 方法
TDD 的核心是测试组件的行为,而不是它们的实现。正如我们在上一节的测试中看到的,为我们想要的行为编写测试使我们能够选择任何能够完成工作的实现。我们专注于重要的部分——组件做什么——而不是不那么重要的细节——它如何做。
在测试中,这表现为调用公共类或包上的公共方法或函数。公共方法是我们选择向更广泛应用程序公开的行为。类、方法或函数中的任何私有数据或支持代码仍然隐藏。
开发人员在学习 TDD 时常犯的一个错误是,他们为了使测试更简单而将某些内容公开。请抵制这种诱惑。这里的一个典型错误是获取一个私有数据字段,并使用公共 getter
方法将其暴露以进行测试。这削弱了该类的封装性。现在,getter
更有可能被滥用。未来的开发人员可能会将真正属于该类的方法添加到其他类中。我们生产代码的设计很重要。幸运的是,有一种简单的方法可以在不损害测试的情况下保持封装性。
保持封装性
如果我们觉得需要为所有私有数据添加 getter
方法,以便测试可以检查每个数据是否符合预期,通常更好的做法是将其视为 值对象(value object)。值对象是一个没有身份的对象。任何两个包含相同数据的值对象都被认为是相等的。使用值对象,我们可以创建另一个包含相同私有数据的对象,然后测试这两个对象是否相等。
在 Java 中,这要求我们为类编写自定义的 equals()
方法。如果我们这样做,还应该编写 hashCode()
方法,因为这两者是相辅相成的。任何有效的实现都可以。我推荐使用 Apache Commons 库,它利用 Java 的反射功能来实现这一点:
@Override
public boolean equals(Object other) {
return EqualsBuilder.reflectionEquals(this, other);
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
你可以在以下链接中找到有关这些库方法的更多信息: https://commons.apache.org/proper/commons-lang/ 。
只需将这两个方法(以及 Apache Commons 库)添加到我们的类中,我们就可以保持所有数据字段的私有性,同时仍然检查所有字段是否包含预期的数据。我们只需创建一个包含所有预期字段的新对象,然后断言它与我们正在测试的对象相等。
当我们编写每个测试时,我们第一次使用被测代码。这使我们能够了解代码的易用性,并在需要时进行更改。