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

测试驱动开发-实例

为已有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);

   }

 

测试驱动的编程是 XP 困扰程序员的一个方面。对于测试驱动的编程意味着什么以及如何去做,大多数人都做出了不正确的假设。这个月,XP 方面的讲师兼 Java 开发人员 Roy Miller 谈论了测试驱动的编程是什么,它为什么可以使程序员的生产力和质量发生巨大变化,以及编写测试的原理。请在与本文相随的 论坛中提出您就本文的想法,以飨笔者和其他读者。(您也可以单击本文顶部或底部的“讨论”来访问该论坛。) 最近 50 年来,测试一直被视为项目结束时要做的事。当然,可以在项目进行之中结合测试测试通常并不是在 所有编码工作结束后才开始,而是一般在稍后阶段进行测试。然而,XP 的提倡者建议完全逆转这个模型。作为一名程序员,应该在编写代码 之前编写测试,然后只编写足以让测试通过的代码即可。这样做将有助于使您的系统尽可能的简单。 先编写测试 XP 涉及两种测试: 程序员测试和 客户测试测试驱动的编程(也称为 测试为先编程)最常指第一种测试,至少我使用这个术语时是这样。测试驱动的编程是让 程序员测试(即单元测试 ― 重申一下,只是换用一个术语)决定您所编写的代码。这意味着您必须在编写代码之前进行测试测试指出您 需要编写的代码,从而也 决定了您要编写的代码。您只需编写足够通过测试的代码即可 ― 不用多,也不用少。XP 规则很简单:如果不进行程序员测试,则您不知道要编写什么代码,所以您不会去编写任何代码。 测试驱动开发(TDD)是极限编程的重要特点,它以不断的测试推动代码的开发,既简化了代码,又保证了软件质量。本文从开发人员使用的角度,介绍了 TDD 优势、原理、过程、原则、测试技术、Tips 等方面。 背景 一个高效的软件开发过程对软件开发人员来说是至关重要的,决定着开发是痛苦的挣扎,还是不断进步的喜悦。国人对软件蓝领的不屑,对繁琐冗长的传统开发过程的不耐,使大多数开发人员无所适从。最近兴起的一些软件开发过程相关的技术,提供一些比较高效、实用的软件过程开发方法。其中比较基础、关键的一个技术就是测试驱动开发(Test-Driven Development)。虽然TDD光大于极限编程,但测试驱动开发完全可以单独应用。下面就从开发人员使用的角度进行介绍,使开发人员用最少的代价尽快理解、掌握、应用这种技术。下面分优势,原理,过程,原则,测试技术,Tips等方面进行讨论。 1. 优势 TDD的基本思路就是通过测试来推动整个开发的进行。而测试驱动开发技术并不只是单纯的测试工作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值