TDD实践之路--泛型和匿名方法在DELPHI单元测试中的妙用
|
昨天启动TDD之旅。开始一切都很顺利,按照“故事情节”,遵守PF,对方法的调用测试很顺利。接着麻烦来了。由于PascalMock没有事件引发的处理机制(特别是我的事件委托都是私有的)造成在测试事件引发时,出现问题。经过一夜的冥思苦想(呵呵),终于找到一个比较合理的处理方法。下一步有时间的话就准备修改PascalMock。由于在PascalMock开发当时,Delphi(WIN32)尚没有引进泛型和匿名方法。所以它使用Variant来处理的(比较土哦)。 思路: 1、把事件封装 2、把通常的事件方法(如:procedure of object)改为匿名方法(如: reference to procedure). 好处多多。最大的好处就是把UNSAFE的POINTER变为了SAFE的REFERENCE. 3、事件封装类引入泛型机制。但有一个问题还不知道怎么处理:就是匿名方法没有一个BASE TYPE(CLASS)。所以泛型无法设定约束(如T: constrain).这时好羡慕C#啊。 代码保留(是以前几篇的PUZZLE为例来做的) unit TestPuzzlePresenter; interface uses TestFrameWork,PascalMock; type TEventDelegateRef = reference to procedure; TEventDelegateClass<T> = class sealed
private
FEventHandler: T;
public
property EventHandler: T read FEventHandler;
constructor Create(Handler: T);
end;
IPuzzleModel = interface
procedure SubscribeUpdateImages(Listener: TEventDelegateClass<TEventDelegateRef>);
procedure LoadImageRequest;
end;
IPuzzleView = interface
procedure SubscribeLoadImageRequest(Listener: TEventDelegateClass<TEventDelegateRef>);
end;
TPuzzleModelMock = class(TMock,IPuzzleModel)
public
procedure SubscribeUpdateImages(Listener: TEventDelegateClass<TEventDelegateRef>);
procedure LoadImageRequest;
end;
TPuzzleViewMock = class(TMock,IPuzzleView)
public
procedure SubscribeLoadImageRequest(Listener: TEventDelegateClass<TEventDelegateRef>);
end;
TPuzzlePresenter = class
private
FModel: IPuzzleModel;
FView: IPuzzleView;
public
procedure AfterConstruction; override;
constructor Create(Model: IPuzzleModel; View: IPuzzleView);
end;
TPuzzlePresenterTest = class(TTestCase)
private
FSubscribeLoadImageRequest: TEventDelegateClass<TEventDelegateRef>;
private
FModelMock: TPuzzleModelMock;
FViewMock: TPuzzleViewMock;
protected
procedure SetUp; override;
procedure TearDown; override;
published
procedure TestLoadImage;
end;
implementation
{ TEventDelegateClass }
constructor TEventDelegateClass<T>.Create(Handler: T);
begin
inherited Create;
FEventHandler := Handler;
end;
{ TPuzzleModelMock }
procedure TPuzzleModelMock.LoadImageRequest;
begin
AddCall('LoadImageRequest');
end;
procedure TPuzzleModelMock.SubscribeUpdateImages(Listener: TEventDelegateClass<TEventDelegateRef>);
begin
AddCall('SubscribeUpdateImages').WithParams([Listener]);
end;
{ TPuzzleViewMock }
procedure TPuzzleViewMock.SubscribeLoadImageRequest(Listener: TEventDelegateClass<TEventDelegateRef>);
begin
AddCall('SubscribeLoadImageRequest').Returns(Listener);
end;
{ TPuzzlePresenter }
procedure TPuzzlePresenter.AfterConstruction;
begin
inherited;
FModel.SubscribeUpdateImages(TEventDelegateClass<TEventDelegateref>.Create(procedure
begin
//Dummy
end));
FView.SubscribeLoadImageRequest(TEventDelegateClass<TEventDelegateRef>.Create(procedure
begin
FModel.LoadImageRequest;
end));
end;
constructor TPuzzlePresenter.Create(Model: IPuzzleModel; View: IPuzzleView);
begin
inherited Create;
FModel := Model;
FView := View;
end;
{ TPuzzlePresenterTest }
procedure TPuzzlePresenterTest.SetUp;
begin
inherited;
FModelMock := TPuzzleModelMock.Create;
FModelMock.Expects('SubscribeUpdateImages').WithParams([Ignored]);
FViewMock := TPuzzleViewMock.Create;
FViewMock.Expects('SubscribeLoadImageRequest').Returns(Ignored);
TPuzzlePresenter.Create(IPuzzleModel(FModelMock),IPuzzleView(FViewMock));
FSubscribeLoadImageRequest := TEventDelegateClass<TEventDelegateRef>(FViewMock.CalledMethodByName('SubscribeLoadImageRequest').ReturnValueAsObject);
end;
procedure TPuzzlePresenterTest.TearDown;
begin
FModelMock.Verify('Model Call Ok?');
FViewMock.Verify('View Call Ok?');
inherited;
end;
procedure TPuzzlePresenterTest.TestLoadImage;
var
Trigger: TEventDelegateRef;
begin
FModelMock.Expects('LoadImageRequest');
Trigger := FSubscribeLoadImageRequest.EventHandler;
Trigger();
end;
initialization
RegisterTest('Puzzle',TPuzzlePresenterTest.Suite);
参考:ms-help://embarcadero.rs_xe/rad/Anonymous_Methods_in_Delphi.html (编辑:安卓应用网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
