您的位置:首页 > 编程语言 > Delphi

TDD实践之路--泛型和匿名方法在DELPHI单元测试中的妙用

2011-08-02 12:51 567 查看
昨天启动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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: