测试驱动开发-实例-笔记

来源:互联网 发布:南欧四猪 知乎 编辑:程序博客网 时间:2024/06/06 20:10

测试驱动开发-实例

为已有Money类添加一个新的功能——加法!

package cn.elena.test2;

 

publicclassMoney {

 

  privateintamount;

  private Stringcurrency;

  Money(int amount, Stringcurrency)

  {

     this.amount=amount;

     this.currency=currency;

  }

  Moneytimes(intmultiplier)

  {

     returnnew Money(amount*multiplier,currency);

  }

  publicboolean equals(Object object)

  {

     Moneymoney = (Money) object;

     returnamount==money.amount

           &&currency().equals(money.currency());

  }

  Stringcurrency()

  {

     returncurrency;

  }

  publicstatic Money dollar(int amount)

  {

     returnnew Money(amount,"USD");

  }

  publicstatic Money Franc(int amount)

  {

     returnnew Money(amount,"CHF");

  }

}

 

测试驱动开发,关键是要在写代码之前写好测试用例,开发过程中维护两套代码(测试代码和实际代码)

 

@Test

  publicvoidtestSimpleAddition()

  {

     Moneysum= Money.dollar(5).plus(Money.dollar(5));

     assertEquals(Money.dollar(10),sum);

  }

写好这个单元测试,马上通过编译器报错就可以生成Moneyplus函数

public Money plus(Money addend) {

     //TODO Auto-generated method stub

     returnnew Money(amount+addend.amount,currency);

  }

要是单元测试通过,只需加上一个return语句。这里没有再一小步一小步的进行伪代码到真实代码的实现。

这里的关键,代表所有货币的Money怎样实现多种币种相加。

解决方法有很多种,在这本书中,解决方案是创建一种行为像是Money类的对象,但是代表两个Money的和。一种比喻,这个类就像一个钱包,各种货币放到钱包中。又或者是一个表达式,Money对象是表达式中无法再继续细分的元素。

 

@Test

  publicvoid testSimpleAddition2()

  {

     Moneyfive = Money.dollar(5);

     Expressionsum = five.plus(five);

     Bankbank = newBank();

     Moneyreduced = bank.reduce(sum,"USD");

     assertEquals(Money.dollar(10),reduced);

  }

一个新的测试用例诞生。书中它是从assertEquals开始写,倒序写到five的创建的。这里还有两个编译错误:ExpressionBank

Expression定义为一个表达式接口(轻量级)。这时候plus函数需要返回一个Expression。这又意味着Money类要实现Expression

 

package cn.elena.test3;

 

publicinterfaceExpression {

 

}

package cn.elena.test3;

 

publicclassBank {

 

  public Money reduce(Expressionsource, String to) {

     //TODO Auto-generated method stub

     return Money.dollar(10);

  }

 

}

Money:

  public Expression plus(Moneyaddend) {

     //TODO Auto-generated method stub

     returnnew Money(amount+addend.amount,currency);

  }

明显这里的Bank用了快速通过单元测试的第一种方法:伪代码。

 

 

首先:Money.plus()需要返回的是一个真正的表达式对象Sum,而不仅仅是一个Money对象。

新的测试用例又来了。。。。。。。

@Test

  publicvoidtestPlusReturnSum()

  {

     Moneyfive = Money.dollar(5);

     Expressionresult = five.plus(five);

     Sumsum=(Sum)result;

     

     assertEquals(five,sum.augend);

     assertEquals(five,sum.addend);

  }

通过修改编译错误获得Sum

package cn.elena.test3;

 

publicclassSum {

 

  Moneyaugend;

  Moneyaddend;

}

运行测试会产生一个ClassCastException,因为plus函数返回的不是一个Sum对象。

Money

public Expression plus(Money addend) {

     //TODO Auto-generated method stub

     returnnew Sum(this,addend);

  }

publicclassSumimplementsExpression{

 

  Moneyaugend;

  Moneyaddend;

  Sum(Moneyaugend,Money addend)

  {

     this.augend=augend;

     this.addend=addend;

     

  }

}

这样这个单元测试就通过了。。。

现在可以向Bank.reduce()传递Sum对象了,如果Sum中的货币和目标货币都一样,那么结果就是一个Money对象,数目就是总和。仍需写好测试再写实现代码。

@Test

  publicvoid testReduceSum()

  {

     Expressionsum = newSum(Money.dollar(3),Money.dollar(4));

     Bankbank = newBank();

     Moneyresult = bank.reduce(sum, "USD");

     assertEquals(Money.dollar(7),result);

  }

Bank:

  public Money reduce(Expressionsource, Stringto){

     //TODO Auto-generated method stub

     Sumsum = (Sum)source;

     int amount = sum.augend.amount+sum.addend.amount;

     returnnew Money(amount ,to);

  }

两个坏味道:

  强制类型转换

  公共域以及对它的二级引用

解决方法:把方法主体移至Sum类且去掉某些可见域。

Bank

publicMoneyreduce(Expression source, String to) {

     //TODO Auto-generated method stub

     Sumsum = (Sum)source;

     return sum.reduce(to);

  }

Sum

public Moneyreduce(Stringto)

  {

     int amount =augend.amount+addend.amount;

     returnnew Money(amount ,to);

  }

这里有个问题,如果参数传进去一个Money怎么办?依旧。。测试先行。。。

@Test

  publicvoid testReduceMoney()

  {

     Bankbank = newBank();

     Money result =bank.reduce(Money.dollar(3),"USD");

     assertEquals(Money.dollar(3),result);

  }

又是类型转换异常,SumMoney都实现了Expression接口。

Bank

public Money reduce(Expression source, String to) {

     if(sourceinstanceof Money)

     {

        return (Money) source;

     }

     Sumsum = (Sum)source;

     return sum.reduce(to);

  }

尽管这样实现很难看,但是运行通过。。接下来就可以进行重构。

无论何时,当我们需要显示地判断是那种累才能进行下一步工作时,都要使用多态来代替。

因为Sum实现了reduce方法,如果Money也实现,那么就可以将它添加到Expression中。

Money

public Money reduce(String to) {

     //TODO Auto-generated method stub

     returnthis;

  }

Bank

public Money reduce(Expression source, String to) {

     if(sourceinstanceof Money)

     {

        return (Money) source.reduce(to);

     }

     Sumsum = (Sum)source;

     return sum.reduce(to);

  }

 

添加成功后,就可以消除所有烦人的强制类型转换和类判定。

Bank

public Money reduce(Expressionsource, String to) {

     returnsource.reduce(to);

  }

 

原创粉丝点击