测试驱动开发是软件开发的重要部分。如果代码不进行测试,是不可靠的。所有代码都必须测试,而且理想情况下应该在编写代码之前编写测试。但是,有些东西容易测试,有些东西不容易。如果要编写一个代表货币值的简单的类,那么很容易测试把 $1.23 和 $2.8 相加是否能够得出 $4.03,而不是 $3.03 或 $4.029999998。测试是否不会出现 $7.465 这样的货币值也不太困难。但是,如何测试把 $7.50 转换为 €5.88 的方法呢(尤其是在通过连接数据库查询随时变动的汇率信息的情况下)?在每次运行程序时,amount.toEuros() 的正确结果都可能有变化。
  答案是 mock 对象。测试并不通过连接真正的服务器来获取新的汇率信息,而是连接一个 mock 服务器,它总是返回相同的汇率。这样可以得到可预测的结果,可以根据它进行测试。毕竟,测试的目标是 toEuros() 方法中的逻辑,而不是服务器是否发送正确的值。(那是构建服务器的开发人员要操心的事)。这种 mock 对象有时候称为 fake。
  mock 对象还有助于测试错误条件。例如,如果 toEuros() 方法试图获取新的汇率,但是网络中断了,那么会发生什么?可以把以太网线从计算机上拔出来,然后运行测试,但是编写一个模拟网络故障的 mock 对象省事得多。
  mock 对象还可以测试类的行为。通过把断言放在 mock 代码中,可以检查要测试的代码是否在适当的时候把适当的参数传递给它的协作者。可以通过 mock 查看和测试类的私有部分,而不需要通过不必要的公共方法公开它们。
  后,mock 对象有助于从测试中消除依赖项。它们使测试更单元化。涉及 mock 对象的测试中的失败很可能是要测试的方法中的失败,不太可能是依赖项中的问题。这有助于隔离问题和简化调试。
  EasyMock 是一个针对 Java 编程语言的开放源码 mock 对象库,可以帮助您快速轻松地创建用于这些用途的 mock 对象。EasyMock 使用动态代理,让您只用一行代码能够创建任何接口的基本实现。通过添加 EasyMock 类扩展,还可以为类创建 mock。可以针对任何用途配置这些 mock,从方法签名中的简单哑参数到检验一系列方法调用的多调用测试。
  EasyMock 简介
  现在通过一个具体示例演示 EasyMock 的工作方式。清单 1 是虚构的 ExchangeRate 接口。与任何接口一样,接口只说明实例要做什么,而不指定应该怎么做。例如,它并没有指定从 Yahoo 金融服务、政府还是其他地方获取汇率数据。
  清单 1. ExchangeRate
  import java.io.IOException;
  public interface ExchangeRate {
  double getRate(String inputCurrency, String outputCurrency) throws IOException;
  }
  清单 2 是假定的 Currency 类的骨架。它实际上相当复杂,很可能包含 bug。(您不必猜了:确实有 bug,实际上有不少)。
  清单 2. Currency 类

 

import java.io.IOException;
public class Currency {
private String units;
private long amount;
private int cents;
public Currency(double amount, String code) {
this.units = code;
setAmount(amount);
}
private void setAmount(double amount) {
this.amount = new Double(amount).longValue();
this.cents = (int) ((amount * 100.0) % 100);
}
public Currency toEuros(ExchangeRate converter) {
if ("EUR".equals(units)) return this;
else {
double input = amount + cents/100.0;
double rate;
try {
rate = converter.getRate(units, "EUR");
double output = input * rate;
return new Currency(output, "EUR");
} catch (IOException ex) {
return null;
}
}
}
public boolean equals(Object o) {
if (o instanceof Currency) {
Currency other = (Currency) o;
return this.units.equals(other.units)
&& this.amount == other.amount
&& this.cents == other.cents;
}
return false;
}
public String toString() {
return amount + "." + Math.abs(cents) + " " + units;
}
}