在Delphi中用TDD :一个简单的实现



最近因工作的关系又再次搞了一些时候的Delphi开发,因为Java的影响也就开始思想如何在Delphi开发中用TDD的方法, 这篇文章就是要谈谈TDD在Delphi中的应用。我想网上已有很多的文章谈到这方面的话题,我这篇也只是谈谈我自己的经验而已。 

那从何说起呢?用个具体的例子会更好的说明问题。试想我们想开个动物繁殖中心,不过我们现阶段还没有特定的动物, 先把这中心搞起来再说,不过中心是离不开动物的,我们就先定义个接口:
  1.  IAnimal = Interface
  2.     procedure Mate;
  3.     procedure Eat;
  4.     procedure GiveBirth;
  5.   End;
这个接口定义了每种动物的三个基本功能:Eat(吃), Mate(交配), GiveBirth(生产), 为了简单起见,我们只处理母的, 公的不在考虑之内。有了接口我们就可以定义繁殖中心:
  1. TAnimalCare= class
  2.   private
  3.     FIntf : IAnimal;
  4.   public
  5.     constructor Create(AnIntf : IAnimal);
  6.     procedure Reproduction;
  7.   end;
TAnimalCare 是处理繁殖的一个类,它需要依靠IAnimal才可以繁殖,不过它并没有锁定任何的动物,
所以在建立时需要注人一个基于IAnimal的类。

有了IAnimal, TAnimalCare 我们需要个测试Project来对它们进行测试,DUnit是Delphi的很好的单元测试工具, 如你用的是Delphi 2007版, DUnit已经在里面, 如用的是早些的版本, 可在 DUnit下载,安装。

建立好Test Project后,我们可以加入下面的这个TestCase:
  1.  TestTAnimalCare = class(TTestCase)
  2.   strict private
  3.     FAnimalCare: TAnimalCare;
  4.   public
  5.     procedure SetUp; override;
  6.     procedure TearDown; override;
  7.   published
  8.     procedure TestReproduction;
  9.   end;
在SetUp这里,需要个基于IAnimal的类才可以,至到目前我们并没有定义一个,同时我们也不需要,因为我们还没有特定的动物。 那么如何来满足这条件以便测试能进行下去呢?这里我们可以用到一种叫Mock的东西, 在Delphi可用 PascalMock, 大家可在这下载, 安装后,我们就可以定义个TAnimalMock:
  1. TAnimalMock = class(TMock, IAnimal)
  2.   public
  3.     procedure Mate;
  4.     procedure Eat;
  5.     procedure GiveBirth;
  6.   end;
  7. procedure TestTAnimalCare.SetUp;
  8. begin
  9.   FMock := TAnimalMock.Create;
  10.   FAnimalCare := TAnimalCare.Create(FMock);
  11. end;
有了TAnimalMock我们已基本满足了编译的要求,可以通过编译。是可以开始TDD的时候了,让我们先完善TestReproduction, 先写些期望,然后verify,看有没有达到:
  1. procedure TestTAnimalCare.TestReproduction;
  2. begin
  3.   // Expectations
  4.   FMock.Expects('Mate');
  5.   FMock.Expects('Eat');
  6.   FMock.Expects('GiveBirth');
  7.   FAnimalCare.Reproduction;
  8.   FMock.Verify('check');
  9. end;
这些期望是很自然的,交配(mate),营养(eat)是生产(GiveBirth)的必须条件,否则是无法繁殖的,我们跑一下测试,失败:
  1. TestReproduction: EMockVerifyException
  2. at  $0049B30A
  3. check
  4. Unexpected call to GiveBirth() <-- Don't match expectations
  5. Expected: 
  6. Mate()
  7. Eat()
  8. GiveBirth()
  9. Called: 
  10. GiveBirth() <-- Don't match expectations
为什么?看看:
  1. procedure TAnimalCare.Reproduction;
  2. begin
  3.   Fintf.GiveBirth;
  4. end;
原来我们只调用GiveBirth而没调用Mate(), Eat(), 不合我们的期望,改成如下:
  1. procedure TAnimalCare.Reproduction;
  2. begin
  3.   FIntF.Mate;
  4.   FIntF.Eat;
  5.   Fintf.GiveBirth;
  6. end;
再跑下测试,成功!

总结:
  1. Dependency Injection, 将基于IAnimal的类注入TAnimalCare, 把有关IAnimal的一些具体的实现从TAnimalCare中分离出来。

  2. Mock来实现一个基于IAnimal的类以便测试.

IAnimal, TAnimalCare码如下:
  1. unit MyObjects;
  2. interface
  3. uses
  4.   classes;
  5. type
  6.   IAnimal = Interface
  7.     procedure Mate;
  8.     procedure Eat;
  9.     procedure GiveBirth;
  10.   End;
  11.   TAnimalCare= class
  12.   private
  13.     FIntf : IAnimal;
  14.   public
  15.     constructor Create(AnIntf : IAnimal);
  16.     procedure Reproduction;
  17.   end;
  18. implementation
  19. { TAnimalCare }
  20. constructor TAnimalCare.Create(AnIntf: IAnimal);
  21. begin
  22.   inherited Create;
  23.   FintF := AnIntf;
  24. end;
  25. procedure TAnimalCare.Reproduction;
  26. begin
  27.   FIntF.Mate;
  28.   FIntF.Eat;
  29.   Fintf.GiveBirth;
  30. end;
  31. end.
测试码如下:
  1. unit TestMyObjects;
  2. interface
  3. uses
  4.   TestFramework, classes, MyObjects, PascalMock;
  5. type
  6.   TAnimalMock = class(TMock, IAnimal)
  7.   public
  8.     procedure Mate;
  9.     procedure Eat;
  10.     procedure GiveBirth;
  11.   end;
  12.   TestTAnimalCare = class(TTestCase)
  13.   strict private
  14.     FMock : TAnimalMock;
  15.     FAnimalCare: TAnimalCare;
  16.   public
  17.     procedure SetUp; override;
  18.     procedure TearDown; override;
  19.   published
  20.     procedure TestReproduction;
  21.   end;
  22. implementation
  23. procedure TestTAnimalCare.SetUp;
  24. begin
  25.   FMock := TAnimalMock.Create;
  26.   FAnimalCare := TAnimalCare.Create(FMock);
  27. end;
  28. procedure TestTAnimalCare.TearDown;
  29. begin
  30.   FAnimalCare.Free;
  31.   FAnimalCare := nil;
  32.   FMock.Free;
  33.   FMock := nil;
  34. end;
  35. procedure TestTAnimalCare.TestReproduction;
  36. begin
  37.   // Expectations
  38.   FMock.Expects('Mate');
  39.   FMock.Expects('Eat');
  40.   FMock.Expects('GiveBirth');
  41.   FAnimalCare.Reproduction;
  42.   FMock.Verify('check');
  43. end;
  44. { TAnimalMock }
  45. procedure TAnimalMock.Eat;
  46. begin
  47.   self.AddCall('Eat');
  48. end;
  49. procedure TAnimalMock.GiveBirth;
  50. begin
  51.   self.AddCall('GiveBirth');
  52. end;
  53. procedure TAnimalMock.Mate;
  54. begin
  55.   self.AddCall('Mate');
  56. end;
  57. initialization
  58.   RegisterTest(TestTAnimalCare.Suite);
  59. end.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值