最近因工作
关系又再次搞了
些时候
Delphi开发
Java
影响也就开始思想如何在Delphi开发中用TDD
思路方法
这篇文章就是要谈谈TDD在Delphi中
应用
我想网上已有很多
文章谈到这方面
话题
我这篇也只是谈谈我自己
经验而已
那从何说起呢?用个具体
例子会更好
介绍说明问题
试想我们想开个动物繁殖中心
不过我们现阶段还没有特定
动物
先把这中心搞起来再说
不过中心是离不开动物
我们就先定义个接口:
="highlighter-c">
IAnimal=Interface
="alt">
procedureMate;
procedureEat;
="alt">
procedureGiveBirth;
End;
这个接口定义了每种动物
3个基本功能:Eat(吃), Mate(交配), GiveBirth(生产), 为了简单起见
我们只处理母
公
不在考虑的内
有了接口我们就可以定义繁殖中心:
="highlighter-delphi">
TAnimalCare=="keyword">
="alt">
="keyword">private
FIntf:IAnimal;
="alt">
="keyword">public
="keyword">constructorCreate(AnIntf:IAnimal);
="alt">
="keyword">procedure
Reproduction;
="keyword">end;
="alt">
TAnimalCare 是处理繁殖
个类
它需要依靠IAnimal才可以繁殖
不过它并没有锁定任何
动物
所以在建立时需要注人
个基于IAnimal
类
有了IAnimal, TAnimalCare 我们需要个测试Project来对它们进行测试
DUnit是Delphi
很好
单元测试工具
如你用
是Delphi 2007版
DUnit已经在里面
如用
是早些
版本
可在DUnit下载
安装
建立好Test Project后
我们可以加入下面
这个TestCase:
="highlighter-delphi">
TestTAnimalCare=="keyword">(TTestCase)
="alt">
strict="keyword">private
FAnimalCare:TAnimalCare;
="alt">
="keyword">public
="keyword">procedureSetUp;override;
="alt">
="keyword">procedure
TearDown;override;
="keyword">published
="alt">
="keyword">procedure
TestReproduction;
="keyword">end;
="alt">
在SetUp这里
需要个基于IAnimal
类才可以
至到目前我们并没有定义
个
同时我们也不需要
我们还没有特定
动物
那么如何来满足这条件以便测试能进行下去呢?这里我们可以用到
种叫Mock
东西
在Delphi可用PascalMock, 大家可在这下载
安装后
我们就可以定义个TAnimalMock:
="highlighter-delphi">
TAnimalMock=="keyword">(TMock,IAnimal)
="alt">
="keyword">public
="keyword">procedureMate;
="alt">
="keyword">procedure
Eat;
="keyword">procedureGiveBirth;
="alt">
="keyword">end
;
="alt">
="keyword">procedure
TestTAnimalCare="number">.
SetUp;
="keyword">begin
="alt">
FMock:=TAnimalMock="number">.
Create;
FAnimalCare:=TAnimalCare="number">.
Create(FMock);
="alt">
="keyword">end
;
有了TAnimalMock我们已基本满足了编译
要求
可以通过编译
是可以开始TDD
时候了
让我们先完善TestReproduction, 先写些期望
然后ver
y
看有没有达到:
="highlighter-delphi">
="keyword">procedureTestTAnimalCare="number">.TestReproduction;
="alt">
="keyword">begin
="comment">//Expectations
="alt">
FMock="number">.
Expects(="
">'Mate'
);
FMock="number">.Expects(="">'Eat');
="alt">
FMock="number">.
Expects(="
">'GiveBirth'
);
="alt">
FAnimalCare="number">.
Reproduction;
FMock="number">.Very(="">'check');
="alt">
="keyword">end;
这些期望是很自然
交配(mate),营养(eat)是生产(GiveBirth)
必须条件
否则是无法繁殖
我们跑
下测试
失败:
="highlighter-xml">
TestReproduction:EMockVeryException
="alt">
at$0049B30A
check
="alt">
UnexpectedcalltoGiveBirth="tag"><
="tag-name">--
Don'tmatchexpectations
="alt">
Expected:
Mate
="alt">
Eat
GiveBirth
="alt">
Called:
="alt">
GiveBirth="tag"><
="tag-name">--
Don'tmatchexpectations
为什么?看看:
="highlighter-delphi">
="keyword">procedureTAnimalCare="number">.Reproduction;
="alt">
="keyword">begin
Ff="number">.GiveBirth;
="alt">
="keyword">end
;
原来我们只
GiveBirth而没
Mate
, Eat
, 不合我们
期望
改成如下:
="highlighter-delphi">
="keyword">procedureTAnimalCare="number">.Reproduction;
="alt">
="keyword">begin
FIntF="number">.Mate;
="alt">
FIntF="number">.
Eat;
Ff="number">.GiveBirth;
="alt">
="keyword">end
;
再跑下测试
成功!
整理总结:
- 用Dependency Injection 将基于IAnimal类注入TAnimalCare, 把有关IAnimal些具体实现从TAnimalCare中分离出来
- 用Mock来实现个基于IAnimal类以便测试.
IAnimal, TAnimalCare码如下:
="highlighter-delphi">
="keyword">unitMyObjects;
="alt">
="keyword">erface
="alt">
="keyword">uses
es;
="alt">
="keyword">type
="alt">
IAnimal=Interface
="keyword">procedureMate;
="alt">
="keyword">procedure
Eat;
="keyword">procedureGiveBirth;
="alt">
End;
="alt">
TAnimalCare=="keyword">
="keyword">private
="alt">
FIntf:IAnimal;
="keyword">public
="alt">
="keyword">constructor
Create(AnIntf:IAnimal);
="keyword">procedureReproduction;
="alt">
="keyword">end
;
="alt">
="keyword">implementation
="alt">
="comment">{TAnimalCare}
="alt">
="keyword">constructor
TAnimalCare="number">.
Create(AnIntf:IAnimal);
="keyword">begin
="alt">
="keyword">inherited
Create;
FF:=AnIntf;
="alt">
="keyword">end
;
="alt">
="keyword">procedure
TAnimalCare="number">.
Reproduction;
="keyword">begin
="alt">
FIntF="number">.
Mate;
FIntF="number">.Eat;
="alt">
Ff="number">.
GiveBirth;
="keyword">end;
="alt">
="keyword">end.
测试码如下:
="highlighter-delphi">
="keyword">unitTestMyObjects;
="alt">
="keyword">erface
="alt">
="keyword">uses
="alt">
TestFramework,es,MyObjects,PascalMock;
="keyword">type
="alt">
TAnimalMock=="keyword">(TMock,IAnimal)
="alt">
="keyword">public
="keyword">procedureMate;
="alt">
="keyword">procedure
Eat;
="keyword">procedureGiveBirth;
="alt">
="keyword">end
;
="alt">
TestTAnimalCare=="keyword">
(TTestCase)
strict="keyword">private
="alt">
FMock:TAnimalMock;
FAnimalCare:TAnimalCare;
="alt">
="keyword">public
="keyword">procedureSetUp;override;
="alt">
="keyword">procedure
TearDown;override;
="keyword">published
="alt">
="keyword">procedure
TestReproduction;
="keyword">end;
="alt">
="keyword">implementation
="alt">
="keyword">procedureTestTAnimalCare="number">.SetUp;
="alt">
="keyword">begin
FMock:=TAnimalMock="number">.Create;
="alt">
FAnimalCare:=TAnimalCare="number">.
Create(FMock);
="keyword">end;
="alt">
="keyword">procedureTestTAnimalCare="number">.TearDown;
="alt">
="keyword">begin
FAnimalCare="number">.Free;
="alt">
FAnimalCare:=="keyword">nil
;
FMock="number">.Free;
="alt">
FMock:=="keyword">nil
;
="keyword">end;
="alt">
="keyword">procedureTestTAnimalCare="number">.TestReproduction;
="alt">
="keyword">begin
="comment">//Expectations
="alt">
FMock="number">.
Expects(="
">'Mate'
);
FMock="number">.Expects(="">'Eat');
="alt">
FMock="number">.
Expects(="
">'GiveBirth'
);
="alt">
FAnimalCare="number">.
Reproduction;
FMock="number">.Very(="">'check');
="alt">
="keyword">end;
="alt">
="comment">{TAnimalMock}
="alt">
="keyword">procedureTAnimalMock="number">.Eat;
="alt">
="keyword">begin
self="number">.AddCall(="">'Eat');
="alt">
="keyword">end
;
="alt">
="keyword">procedure
TAnimalMock="number">.
GiveBirth;
="keyword">begin
="alt">
self="number">.
AddCall(="
">'GiveBirth'
);
="keyword">end;
="alt">
="keyword">procedureTAnimalMock="number">.Mate;
="alt">
="keyword">begin
self="number">.AddCall(="">'Mate');
="alt">
="keyword">end
;
="alt">
="keyword">initialization
RegisterTest(TestTAnimalCare="number">.Suite);
="alt">
="keyword">end
.
延伸阅读
最新评论