虽然我们构建一个这个样的类,但是你能忍受这个测试的速度,根据《Working Effectively with legacy Code 》书中提到大于1秒的单元测试,都不叫单元测试。因此在测试中建立到服务器的连接并不是一个好的主意。首先其好事比较长,况且服务器也并不总是处于服务状态。可想而知RGHConnection是一个令人恼火的参数。我们的设想是:若能创建某种伪造的RGHConnection对象并使CreditValidator相信它是一个真正的RGHConnection的话,可以避开所有链接的问题了。

  首先来看一下RGHConnection所拥有的方法:

RGHConnection
+ RGHConnection(port,name,password)
+ connect()
+ disconnect()
+RFDIReportFor(id:int):RFDIReport
+ACTIOReportFor(customerID:int) ACTIOReport
+retry()
+fromPacket():RFPacket

  看上去RGHConnection中有一些方法是用来处理与连接相关的任务的:如connect、disconnect以及retry。另外还有一些业务方法。因此如果要伪造一个RGHConnection对象的话,那么这个伪造的对象也必须拥有这些方法也能提供一样的信息。

  在这些条件下,伪造一个RGHConnection对象好的方法是对RGHConnection类应用接口提取。如果你手头一个支持重构的工具,那么它很可能也会支持接口提取方法。我们来看一下接口提取后的情况:

<interface>
IRGHConnection
+connect()
+ disconnect()
+RFDIReportFor(id:int):RFDIReport
+ACTIOReportFor(customerID:int) ACTIOReport


  由于retry()和fromPacket()不属于业务相关方法因此只需要在实现类中增加这两个方法,至此我们可以轻松的构建出一个FackeConnection类,并使它能够提供我们所需要的反馈信息,然后将这个伪造的对象用在测试中:

public class FakeConnection implements IRGHConnection
{
    public  RFDIReport report;
    public void connect() {}
    public void disconnnect(){}
    public RFDIReport RFDReportFor(int id) {return report;}
    public ACTIOReport ACTIOReportFor(int customerID) {return null;}
}

  下面我们来写测试

void testNoSuccess()throws Exception{
  CreditMaster master = new CreditMaster("crm2.mas",true);
  IRGHConnection connection = new FakeConnection();
  CreditValidator validator = new CreditValidator(connection,master,"a");
  connection.report = new RFDReport(....);
  Certificate result = validator.validatorCustomer(new Customer(...));
  assertEquals(Certificate.VALID,result.getStatus());
}

  虽然FakeConnection类看起来有点奇怪:它的方法要么是空的要么简单的返回null。这种情形并不常见。更糟的是,它有一个任何人都可以看到的并随意设置的公共变量。这样一个类似似乎违反了所有的良好准则。但你要看到,实际上并非如此。对于一个用来使得测试可行的类,规则是所不同的。FakeConnection中的代码并非产品代码。它永远也不属于终投入运行的应用,而只是为了测试工具和测试而诞生。

  有了这个fake类我们接下去便可以做更多的相关测试。这是提高代码可测试性和遇到教难构造的的类的时候所采取的一种方法。若代码设计阶段将RGHConnection设计为接口,那么在后面的测试中会是测试更加方便,使代码在后期的重构也会更加方便。