Unit testing Delphi单元测试:为CUT编写一个简单的spy

Unit testing Delphi单元测试:为CUT编写一个简单的spy,unit-testing,delphi,spy,dunit,dunitx,Unit Testing,Delphi,Spy,Dunit,Dunitx,我正在寻找一种方法,在Delphi下为DUnitX测试框架编写一个spy 在过去,我使用了非常丑陋的方法,使用: [TestFixture] Test = class(TObject) public [test] procedure Test1; end; TMyClass = class(TObject) protected procedure MyProcedure; virtual; end; TMyTestClass = class(TMyClass) protected

我正在寻找一种方法,在Delphi下为DUnitX测试框架编写一个spy

在过去,我使用了非常丑陋的方法,使用:

[TestFixture]
Test = class(TObject)
public
  [test]
  procedure Test1;
end;

TMyClass = class(TObject)
protected
  procedure MyProcedure; virtual;
end;

TMyTestClass = class(TMyClass)
protected
  fMyProcedureCalled : Boolean;
  procedure MyProcedure; override;
end

procedure TMyTestClass.MyProcedure;
begin
   fMyProcedureCalled := true;
   inherited;
end;

procedure Test.Test1;
var aObj : TMyTestClass;
begin
   TMyTestClass.Create;
   Assert.IsTrue(aObj.fMyProcedureCalled);
end;
所有这些代码都用于检查是否调用了过程。那太冗长了


有没有办法编写一个spy来帮助我减少代码量?

听起来像是模拟的用例(我在这里使用mock这个术语,因为大多数框架都将各种类型的测试加倍称为mock)

在下面的示例中,我使用的是DUnit,但它不会对DUnitX产生任何影响。我还使用了Spring4D1.2中的模拟功能(我没有检查DelphiMocks是否支持此功能)

unitmyclass;
接口
类型
TMyClass=类
私有的
fCounter:整数;
受保护的
程序我的程序;事实上的
公众的
属性计数器:整数读取fCounter;
结束;
实施
程序TMyClass.MyProcedure;
开始
公司(fCounter);
结束;
结束。
程序测试;
使用
测试框架,
Destinsight.DUnit,
春天,嘲笑,
“MyClass.pas”中的MyClass;
类型
TMyClass=class(MyClass.TMyClass)
公众的
//只是为了便于测试
程序我的程序;推翻
结束;
TMyTest=类(TTestCase)
出版
程序测试1;
结束;
程序TMyClass.MyProcedure;
开始
继承;
结束;
程序TMyTest.Test1;
变量
//模拟在第一次使用时自动初始化
//默认为TMockBehavior.Dynamic,这意味着它允许所有调用发生
m:模拟;
o:TMyClass;
开始
//将其设置为true以实际调用“real”方法
m、 CallBase:=True;
//用o做点什么
o:=m;
o、 我的程序;
//检查预期呼叫是否确实发生
m、 收到(次。一次)。我的程序;
//证明它确实调用了“真实”方法
CheckEquals(1,o.计数器);
结束;
开始
注册测试(TMyTest.Suite);
RunRegisteredTests();
结束。

请记住,这只适用于虚拟方法。

如果需要测试基类中不受
$RTTI
指令影响的方法,可以使用一些小技巧,在
public
部分的子类中将其重新定义为
覆盖;摘要这将导致生成RTTI。谢谢你们的回答。我不知道Spring4d有这种模拟功能。非常方便!
unit MyClass;

interface

type
  TMyClass = class
  private
    fCounter: Integer;
  protected
    procedure MyProcedure; virtual;
  public
    property Counter: Integer read fCounter;
  end;

implementation

procedure TMyClass.MyProcedure;
begin
  Inc(fCounter);
end;

end.

program Tests;

uses
  TestFramework,
  TestInsight.DUnit,
  Spring.Mocking,
  MyClass in 'MyClass.pas';

type
  TMyClass = class(MyClass.TMyClass)
  public
    // just to make it accessible for the test
    procedure MyProcedure; override;
  end;

  TMyTest = class(TTestCase)
  published
    procedure Test1;
  end;

procedure TMyClass.MyProcedure;
begin
  inherited;
end;

procedure TMyTest.Test1;
var
  // the mock is getting auto initialized on its first use
  // and defaults to TMockBehavior.Dynamic which means it lets all calls happen
  m: Mock<TMyClass>;
  o: TMyClass;
begin
  // set this to true to actually call the "real" method
  m.CallBase := True;
  // do something with o
  o := m;
  o.MyProcedure;

  // check if the expected call actually did happen
  m.Received(Times.Once).MyProcedure;

  // to prove that it actually did call the "real" method
  CheckEquals(1, o.Counter);
end;

begin
  RegisterTest(TMyTest.Suite);
  RunRegisteredTests();
end.