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

测试驱动开发-实例

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

package cn.elena.test2;

 

public classMoney {

 

   private int amount;

   private String currency;

   Money(int amount, Stringcurrency)

   {

      this.amount=amount;

      this.currency=currency;

   }

   Moneytimes(intmultiplier)

   {

      return new Money(amount*multiplier, currency);

   }

   public boolean equals(Object object)

   {

      Moneymoney = (Money) object;

      return amount==money.amount

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

   }

   Stringcurrency()

   {

      return currency;

   }

   public static Money dollar(int amount)

   {

      return new Money(amount,"USD");

   }

   public static Money Franc(int amount)

   {

      return new Money(amount,"CHF");

   }

}

 

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

 

@Test

   public void testSimpleAddition()

   {

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

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

   }

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

public Money plus(Money addend) {

      // TODO Auto-generated method stub

      return new Money(amount+addend.amount,currency);

   }

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

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

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

 

@Test

   public void testSimpleAddition2()

   {

      Moneyfive = Money.dollar(5);

      Expressionsum = five.plus(five);

      Bankbank = newBank();

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

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

   }

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

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

 

package cn.elena.test3;

 

public interfaceExpression {

 

}

package cn.elena.test3;

 

public classBank {

 

   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

      return new Money(amount+addend.amount,currency);

   }

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

 

 

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

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

@Test

   public void testPlusReturnSum()

   {

      Moneyfive = Money.dollar(5);

      Expressionresult = five.plus(five);

      Sumsum=(Sum)result;

     

      assertEquals(five,sum.augend);

      assertEquals(five,sum.addend);

   }

通过修改编译错误获得Sum

package cn.elena.test3;

 

public classSum {

 

   Moneyaugend;

   Moneyaddend;

}

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

Money

public Expression plus(Money addend) {

      // TODO Auto-generated method stub

      return new Sum(this,addend);

   }

public classSum implementsExpression{

 

   Moneyaugend;

   Moneyaddend;

   Sum(Moneyaugend,Money addend)

   {

      this.augend=augend;

      this.addend=addend;

     

   }

}

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

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

@Test

   public void 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, String to){

      // TODO Auto-generated method stub

      Sumsum = (Sum)source;

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

      return new Money(amount ,to);

   }

两个坏味道:

   强制类型转换

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

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

Bank

public Moneyreduce(Expression source, String to) {

      // TODO Auto-generated method stub

      Sumsum = (Sum)source;

      return sum.reduce(to);

   }

Sum

public Money reduce(Stringto)

   {

      int amount = augend.amount+addend.amount;

      return new Money(amount ,to);

   }

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

@Test

   public void 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(source instanceof 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

      return this;

   }

Bank

public Money reduce(Expression source, String to) {

      if(source instanceof Money)

      {

         return (Money) source.reduce(to);

      }

      Sumsum = (Sum)source;

      return sum.reduce(to);

   }

 

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

Bank

public Money reduce(Expression source, String to) {

      return source.reduce(to);

   }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值