假设您有一个设计精良的Delphi项目,该项目尊重依赖注入和其他一些良好实践。
现在让我们假设你需要模拟一个定义为:
的类TMyClass = class
public
procedure Method1;
procedure Method2
end;
Method1
和Method2
不是虚拟的。在这种情况下你做什么?要模拟对象,我们需要继承它并override
要模拟的每个方法,但在这种情况下不可能因为它们不是virtual
。我是否应该更改源代码以在我需要模拟的每个方法上添加virtual
?不是很糟糕吗?
修改
我正在考虑创建一个编译器指令,使类中的所有字段都为virtual
,这是一个很好的意识形态吗?只有我的测试套件才会设置编译器指令。
EDIT2 *
Embarcadero应该提供一种简单的方法,可以将类的方法指针更改为另一个方法点,而无需virtual
。
答案 0 :(得分:12)
使方法成为虚拟,以便您可以模拟它们。 (它们不需要是抽象的。)
如果你不能这样做,那么用另一个类包装类。使包装器的方法成为虚拟的,并且在默认实现中,只需将调用调用转发给原始类。只要程序使用原始类,请将其替换为包装器。现在,模拟包装器。
答案 1 :(得分:6)
要模拟我们需要继承的对象 它,但这是不可能的 情况下。
我建议你说“我需要模仿这个”的每个课程都应该基于一个界面。
换句话说,如果您有需要模拟的方法,则应将它们放入接口,然后要模拟的类实现该接口。然后,通过在模拟对象中实现相同的接口来创建模拟。
另一种方法是使用Mocking库。你可以看看这些问题:
What is your favorite Delphi mocking library?
这个框架也包括模拟对象:
http://code.google.com/p/emballo/
模拟对象还应该鼓励在代码中正确使用依赖注入。
答案 2 :(得分: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;
在使用“TMyClass”的地方,将其更改为使用TMyClassbase:
TMyOtherClass = class(TMyOtherClassBase)
private
FMyThing:TMyClassbase;
public
property MyThing:TMyClassBase read FMyThing write FMyThing;
end;
通过在运行时连接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
(不要忘记稍后解开MyOtherClassObj.MyThing,否则你会有泄漏)