2. 变质的对象
测试驱动开发的总体流程如下:
- 写一个测试程序。
- 让测试程序运行。
- 编写合格的代码。
计划清单(to-do list)
当瑞士法郎与美元的兑换率为 2:1 的时候,5 美元 + 10 瑞士法郎 = 10 美元
5 美元 * 2 = 10 美元将 "amount" 定义为私有
Dollar 类有副作用吗?
钱数必须为整数?
在 Dollar 对象上施加一个操作后,Dollar 对象改变了。假设代码是:
public void testMultiplication() {
Dollar five = new Dollar(5);
five.times(2);
assertEquals(10, five.amount);
five.times(3);
assertEquals(15, five.amount);
}
第一次调用 times() 函数后,five 已经不再是 5 了,它实际上是 10。如果我们从 times() 函数返回一个新对象,那么我们可以多次对原来的 five 进行操作,但不会让它发生丝毫变化。
public void testMultiplication() {
Dollar five = new Dollar(5);
Dollar product = five.times(2);
assertEquals(10, product.amount);
five.times(3);
assertEquals(15, product.amount);
}
改变 Dollar.times() 函数声明,让新的测试程序编译通过。
Dollar times(int multiplier) {
amount *= multiplier;
return null;
}
测试程序可以编译了,但无法运行。需要返回一个新的带有正确的 amount 值的 Dollar 对象:
Dollar times(int multiplier) {
return new Dollar(amount * multiplier);
}
测试程序运行通过,下面是尽快使测试程序可运行的三条策略:
- 伪实现——返回一个常量并逐渐用变量替代常量,直至伪实现代码成为真实实现的代码
- 显明实现(Obvious Implementation)——将真实的实现代码键入
- 三角法(Triangulation)——当例子达到 2 个或更多时才对代码实施一般化
本章工作回顾:
- 将一个设计缺陷(副作用)转化为一个由此缺陷导致运行失败的测试程序。
- 采用存根实现使代码迅速编译通过。
- 键入我们认为正确的代码使测试程序尽快工作。