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



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

那从何说起呢?用个具体例子会更好介绍说明问题试想我们想开个动物繁殖中心不过我们现阶段还没有特定动物先把这中心搞起来再说不过中心是离不开动物我们就先定义个接口:
    ="highlighter-c">
  1. IAnimal=Interface
  2. ="alt">procedureMate;
  3. procedureEat;
  4. ="alt">procedureGiveBirth;
  5. End;

这个接口定义了每种动物 3个基本功能:Eat(吃), Mate(交配), GiveBirth(生产), 为了简单起见我们只处理母不在考虑的内有了接口我们就可以定义繁殖中心:
    ="highlighter-delphi">
  1. TAnimalCare=="keyword">
  2. ="alt">="keyword">private
  3. FIntf:IAnimal;
  4. ="alt">="keyword">public
  5. ="keyword">constructorCreate(AnIntf:IAnimal);
  6. ="alt">="keyword">procedureReproduction;
  7. ="keyword">end;
  8. ="alt">

TAnimalCare 是处理繁殖个类它需要依靠IAnimal才可以繁殖不过它并没有锁定任何动物
所以在建立时需要注人个基于IAnimal



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

建立好Test Project后我们可以加入下面这个TestCase:
    ="highlighter-delphi">
  1. TestTAnimalCare=="keyword">(TTestCase)
  2. ="alt">strict="keyword">private
  3. FAnimalCare:TAnimalCare;
  4. ="alt">="keyword">public
  5. ="keyword">procedureSetUp;override;
  6. ="alt">="keyword">procedureTearDown;override;
  7. ="keyword">published
  8. ="alt">="keyword">procedureTestReproduction;
  9. ="keyword">end;
  10. ="alt">


在SetUp这里需要个基于IAnimal类才可以至到目前我们并没有定义同时我们也不需要我们还没有特定动物那么如何来满足这条件以便测试能进行下去呢?这里我们可以用到种叫Mock东西在Delphi可用PascalMock, 大家可在这下载安装后我们就可以定义个TAnimalMock:
    ="highlighter-delphi">
  1. TAnimalMock=="keyword">(TMock,IAnimal)
  2. ="alt">="keyword">public
  3. ="keyword">procedureMate;
  4. ="alt">="keyword">procedureEat;
  5. ="keyword">procedureGiveBirth;
  6. ="alt">="keyword">end;
  7. ="alt">="keyword">procedureTestTAnimalCare="number">.SetUp;
  8. ="keyword">begin
  9. ="alt">FMock:=TAnimalMock="number">.Create;
  10. FAnimalCare:=TAnimalCare="number">.

    Create(FMock);
  11. ="alt">="keyword">end;


有了TAnimalMock我们已基本满足了编译要求可以通过编译是可以开始TDD时候了让我们先完善TestReproduction, 先写些期望然后very看有没有达到:
    ="highlighter-delphi">
  1. ="keyword">procedureTestTAnimalCare="number">.TestReproduction;
  2. ="alt">="keyword">begin
  3. ="comment">//Expectations
  4. ="alt">FMock="number">.Expects(="">'Mate');
  5. FMock="number">.Expects(="">'Eat');
  6. ="alt">FMock="number">.Expects(="">'GiveBirth');
  7. ="alt">FAnimalCare="number">.Reproduction;
  8. FMock="number">.Very(="">'check');
  9. ="alt">
  10. ="keyword">end;


这些期望是很自然交配(mate),营养(eat)是生产(GiveBirth)必须条件否则是无法繁殖我们跑下测试失败:
    ="highlighter-xml">
  1. TestReproduction:EMockVeryException
  2. ="alt">at$0049B30A
  3. check
  4. ="alt">UnexpectedcalltoGiveBirth="tag"><="tag-name">--Don'tmatchexpectations
  5. ="alt">Expected:
  6. Mate
  7. ="alt">Eat
  8. GiveBirth
  9. ="alt">
  10. Called:
  11. ="alt">GiveBirth="tag"><="tag-name">--Don'tmatchexpectations

为什么?看看:
    ="highlighter-delphi">
  1. ="keyword">procedureTAnimalCare="number">.Reproduction;
  2. ="alt">="keyword">begin
  3. Ff="number">.GiveBirth;
  4. ="alt">="keyword">end;


原来我们只GiveBirth而没Mate, Eat, 不合我们期望改成如下:

    ="highlighter-delphi">
  1. ="keyword">procedureTAnimalCare="number">.Reproduction;
  2. ="alt">="keyword">begin
  3. FIntF="number">.Mate;
  4. ="alt">FIntF="number">.Eat;
  5. Ff="number">.GiveBirth;
  6. ="alt">="keyword">end;


再跑下测试成功!

整理总结:
  1. 用Dependency Injection 将基于IAnimal类注入TAnimalCare, 把有关IAnimal些具体实现从TAnimalCare中分离出来
  2. 用Mock来实现个基于IAnimal类以便测试.


IAnimal, TAnimalCare码如下:
    ="highlighter-delphi">
  1. ="keyword">unitMyObjects;
  2. ="alt">
  3. ="keyword">erface
  4. ="alt">="keyword">uses
  5. es;
  6. ="alt">
  7. ="keyword">type
  8. ="alt">IAnimal=Interface
  9. ="keyword">procedureMate;
  10. ="alt">

    ="keyword">procedureEat;
  11. ="keyword">procedureGiveBirth;
  12. ="alt">End;
  13. ="alt">TAnimalCare=="keyword">
  14. ="keyword">private
  15. ="alt">FIntf:IAnimal;
  16. ="keyword">public
  17. ="alt">="keyword">constructorCreate(AnIntf:IAnimal);
  18. ="keyword">procedureReproduction;
  19. ="alt">="keyword">end;
  20. ="alt">="keyword">implementation
  21. ="alt">="comment">{TAnimalCare}
  22. ="alt">="keyword">constructorTAnimalCare="number">.Create(AnIntf:IAnimal);
  23. ="keyword">begin
  24. ="alt">="keyword">inheritedCreate;
  25. FF:=AnIntf;
  26. ="alt">="keyword">end;
  27. ="alt">="keyword">procedureTAnimalCare="number">.Reproduction;
  28. ="keyword">begin
  29. ="alt">FIntF="number">.Mate;
  30. FIntF="number">.Eat;
  31. ="alt">Ff="number">.GiveBirth;
  32. ="keyword">end;
  33. ="alt">
  34. ="keyword">end.




测试码如下:
    ="highlighter-delphi">
  1. ="keyword">unitTestMyObjects;
  2. ="alt">
  3. ="keyword">erface
  4. ="alt">
  5. ="keyword">uses
  6. ="alt">TestFramework,es,MyObjects,PascalMock;
  7. ="keyword">type
  8. ="alt">
  9. TAnimalMock=="keyword">(TMock,IAnimal)
  10. ="alt">="keyword">public
  11. ="keyword">procedureMate;
  12. ="alt">="keyword">procedureEat;
  13. ="keyword">procedureGiveBirth;
  14. ="alt">="keyword">end;
  15. ="alt">TestTAnimalCare=="keyword">(TTestCase)
  16. strict="keyword">private
  17. ="alt">FMock:TAnimalMock;
  18. FAnimalCare:TAnimalCare;
  19. ="alt">="keyword">public
  20. ="keyword">procedureSetUp;override;
  21. ="alt">="keyword">procedureTearDown;override;
  22. ="keyword">published
  23. ="alt">="keyword">procedureTestReproduction;
  24. ="keyword">end;

  25. ="alt">
  26. ="keyword">implementation
  27. ="alt">
  28. ="keyword">procedureTestTAnimalCare="number">.SetUp;
  29. ="alt">="keyword">begin
  30. FMock:=TAnimalMock="number">.Create;
  31. ="alt">FAnimalCare:=TAnimalCare="number">.Create(FMock);
  32. ="keyword">end;
  33. ="alt">
  34. ="keyword">procedureTestTAnimalCare="number">.TearDown;
  35. ="alt">="keyword">begin
  36. FAnimalCare="number">.Free;
  37. ="alt">FAnimalCare:=="keyword">nil;
  38. FMock="number">.Free;
  39. ="alt">FMock:=="keyword">nil;
  40. ="keyword">end;
  41. ="alt">
  42. ="keyword">procedureTestTAnimalCare="number">.TestReproduction;
  43. ="alt">="keyword">begin
  44. ="comment">//Expectations
  45. ="alt">FMock="number">.Expects(="">'Mate');
  46. FMock="number">.Expects(="">'Eat');
  47. ="alt">FMock="number">.Expects(="">'GiveBirth');
  48. ="alt">FAnimalCare="number">.Reproduction;
  49. FMock="number">.Very(="">'check');
  50. ="alt">
  51. ="keyword">end;
  52. ="alt">
  53. ="comment">{TAnimalMock}
  54. ="alt">
  55. ="keyword">procedureTAnimalMock="number">.Eat;
  56. ="alt">="keyword">begin
  57. self="number">.AddCall(="">'Eat');
  58. ="alt">="keyword">end;
  59. ="alt">="keyword">procedureTAnimalMock="number">.GiveBirth;
  60. ="keyword">begin
  61. ="alt">self="number">.AddCall(="">'GiveBirth');
  62. ="keyword">end;
  63. ="alt">
  64. ="keyword">procedureTAnimalMock="number">.Mate;
  65. ="alt">="keyword">begin
  66. self="number">.AddCall(="">'Mate');
  67. ="alt">="keyword">end;
  68. ="alt">="keyword">initialization
  69. RegisterTest(TestTAnimalCare="number">.Suite);
  70. ="alt">="keyword">end.


Tags:  delphi教程 delphi盒子 delphi7 delphi

延伸阅读

最新评论

发表评论