如何模拟没有虚方法的类?

Raf*_*cci 7 delphi unit-testing mocking

假设您有一个设计精良的Delphi项目,该项目尊重依赖注入和其他一些良好实践.

现在让我们假设你需要模拟一个定义为的类:

TMyClass = class
public
  procedure Method1;
  procedure Method2
end;  
Run Code Online (Sandbox Code Playgroud)

Method1而且Method2不是虚拟的.在这种情况下你做什么?要模拟一个对象,我们需要继承它和override你想要模拟的每个方法,但在这种情况下它是不可能的,因为它们不是virtual.我应该更改源代码以添加virtual我需要模拟的每个方法吗?不是很糟糕吗?

编辑

我正在考虑创建一个编译器指令来使类中的所有字段virtual成为一个好的意识形态?只有我的测试套件才会设置编译器指令.

EDIT2*

Embarcadero应该提供一种简单的方法,可以将类的方法指针更改为另一个方法点,而无需使用virtual.

Rob*_*edy 12

使方法成为虚拟,以便您可以模拟它们.(他们不需要抽象.)

如果你不能这样做,那么用另一个类包装该类.使包装器的方法成为虚拟的,并且在默认实现中,只需将调用调用转发给原始类.只要程序使用原始类,请将其替换为包装器.现在,模拟包装器.


Nic*_*ges 6

要模拟一个对象,我们需要继承它,但在这种情况下是不可能的.

我建议你说"我需要模仿这个"的每个课程都应该基于一个界面.

换句话说,如果您有需要模拟的方法,则应将它们放入接口,然后要模拟的类实现该接口.然后,通过在模拟对象中实现相同的接口来创建模拟.

另一种方法是使用Mocking库.你可以看看这些SO问题:

你最喜欢的Delphi模拟库是什么?

而且这个框架也包括模拟对象:

http://code.google.com/p/emballo/

模拟对象还应该鼓励在代码中正确使用依赖注入.

  • @Warren:没有"要求"使用Delphi的interface关键字进行引用计数.您只需在接口的\ _AddRef和\ _Release方法中返回-1即可禁用引用计数机制.请注意,当您还具有_do_使用引用计数进行终身管理的接口对象时,您会引入歧义. (2认同)

War*_* P 5

您不仅应该使方法成为虚拟,还应该声明纯虚基类,并使其他类仅使用纯虚基类名.现在你可以使用我们称之为"Liskov替换原则"的东西,你可以根据需要制作抽象基类的具体子类型.由于抽象基类的工作原理就像接口在delphi中工作一样,减去引用计数部分,因此您可以获得两全其美的效果.你可以真正保持你的应用程序代码简单,并以这种方式减少错误耦合,另外你可以获得自己组合在一起的单元可测试的"复合对象".

// in UnitBase.pas
TMyClassBase = class
public
  procedure Method1; virtual; abstract;
  procedure Method2; virtual; abstract;
end; 

// in UnitReal.pas
TMyClassReal = class(TMyClassbase)
public
  procedure Method1; override;
  procedure Method2; override;
end; 

// in UnitMock.pas 
TMyClassMock = class(TMyClassbase)
public
  procedure Method1; override;
  procedure Method2; override;
end; 
Run Code Online (Sandbox Code Playgroud)

在使用"TMyClass"的地方,将其更改为使用TMyClassbase:

TMyOtherClass = class(TMyOtherClassBase)
private
  FMyThing:TMyClassbase;
public
  property MyThing:TMyClassBase read FMyThing write FMyThing;
end;
Run Code Online (Sandbox Code Playgroud)

通过在运行时连接TMyOtherclass,您可以决定是使用real类还是mock类:

// in my realapp.pas
MyOtherClassObj :=   TMyotherClass.Create;
MyOtherClassObj.MyThing :=  TMyOtherClassReal.Create;  // real object

// in my unittest.pas
MyOtherClassObj :=   TMyotherClass.Create;
MyOtherClassObj.MyThing :=  TMyOtherClassMock.Create;  // mock object
Run Code Online (Sandbox Code Playgroud)

(不要忘记稍后解开MyOtherClassObj.MyThing的方法,否则你会有泄漏)

  • 制作抽象基类有什么好处?你提到有多个子类型,但是应用程序已经可以使用*no*subtypes,那么为什么要花费额外的努力来引入更多的继承呢? (2认同)