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


 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值