生成的 AssertionFailedError
告诉我结果不是 31 而是 0。这个失败在预料之中,因为我没有改变 getAge() 方法来做某些不同的事。现在仅仅编写足够使测试通过的代码(这里有两个测试)。我必须允许年龄的缺省值为 0,但我必须计算出生于 1971 年 3 月 23 日的人的年龄。一些程序员(包括 Kent Beck)建议在这一点上尽可能简单,譬如检查 birthdate
,看它是否为 null ― 如果为 null,则返回 0,否则返回 31 ― 然后编写另一个测试使计算更智能。一小步一小步地思考问题这种方法是很好的技术,我们要采用这种技术,当您想回到上面提到的基本规程来使自己摆脱调试惯例时,那是再好不过。但这里我想使该示例略微简单些,所以我仅仅试图通过按我所希望的方式,用 Calendar
计算年龄,使该测试通过。清单 5 显示了 Person
中我所编写的代码:
清单 5. getAge() 实现
package com.roywmiller.testexample; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; public class Person { protected Date birthdate; public int getAge() { if (birthdate == null) return 0; else { int yearToday = Calendar.getInstance().get(Calendar.YEAR); Calendar calendar = new GregorianCalendar(); calendar.setTime(birthdate); int birthYear =calendar.get(Calendar.YEAR); return yearToday - birthYear; } } public void setBirthDate(Date aBirthDate) { this.birthdate = aBirthDate; } } |
当我运行测试时,我失败了,预期的结果为 31,但实际结果为 32。怎么了?唔,我知道问题一定出在刚才所写的代码中,没有进一步考虑下去。在检查完 else
子句之后,我明白我只是根据年来计算年龄。这不对。我现在 31 岁,但这个月再过几天我要 32 岁了(但我写该代码时,是 3 月份),我的算法造成错误的结果。所以需要重新考虑 getAge()
。我用清单 6 中的代码段纠正了这个错误:
清单 6. 改正后的 getAge()
else { int yearToday = Calendar.getInstance().get(Calendar.YEAR); Calendar calendar = new GregorianCalendar(); calendar.setTime(birthdate); int birthYear = calendar.get(Calendar.YEAR); if (yearToday == birthYear) return yearToday - birthYear; else return yearToday - birthYear - 1; } |
绿条!在 Person
类中有一些重复的代码,但我把它留给稍后的重构练习。欢迎替我清理该代码。您可以有信心地做这件事,因为可以运行测试来证实您没有破坏任何事物。
这个示例使您体会到了测试驱动的编程类似于什么。我只在每步编写足够让测试通过的代码。作为一种理论,这在思想倾向上是一种挑战。您必须习惯这种思想,在编写代码 之前,可以并应该编写测试。在通过所有测试之后,就完成了工作。
在先编写测试时,必须习惯故意只看眼前。清单 6 中的示例是一个十分简单的情形。即使最简单的编程问题,在实际当中通常要更复杂。这种方法有助于将问题分解成更可管理的部分,但您最终仍可能遇到一些复杂的令人头疼的问题。在那些情况下,必须使自己不要考虑太远,不要假定它的“普适性”有多高,也不要假定这种方法能处理某些尚未遇到的情形。仅仅编写测试,使它通过。您需要采取一些较小的步骤,然后编写迫使您要采取更多步骤的测试。请记住,您正在测试代码的存在性,如果您以较小的步骤来编写代码,那您就做对了。