正如我所看到的,根本问题是TMock<T>.Create
导致被测试类(CUT)被实例化。我怀疑这个框架的设计是基于假设你会嘲笑一个抽象基类。在这种情况下,实例化它将是良性的。我怀疑你正在处理遗留的代码,它没有CUT的方便的抽象基类。但在你的情况下,实例化CUT的唯一方法就是将参数传递给构造函数,从而打败了嘲笑的全部目的。而且我想象一下,重新设计遗留代码库将需要很多工作,直到您为需要模拟的所有类抽象基类为止。
您正在撰写TMock<TFoo>.Create
其中TFoo
是一类。这会导致创建代理对象。这发生在TObjectProxy<T>.Create
。其代码如下所示:
constructor TObjectProxy<T>.Create;
var
ctx : TRttiContext;
rType : TRttiType;
ctor : TRttiMethod;
instance : TValue;
begin
inherited;
ctx := TRttiContext.Create;
rType := ctx.GetType(TypeInfo(T));
if rType = nil then
raise EMockNoRTTIException.Create('No TypeInfo found for T');
ctor := rType.GetMethod('Create');
if ctor = nil then
raise EMockException.Create('Could not find constructor Create on type ' + rType.Name);
instance := ctor.Invoke(rType.AsInstance.MetaclassType, []);
FInstance := instance.AsType<T>();
FVMInterceptor := TVirtualMethodInterceptor.Create(rType.AsInstance.MetaclassType);
FVMInterceptor.Proxify(instance.AsObject);
FVMInterceptor.OnBefore := DoBefore;
end;
正如你所看到的代码假设你的类有一个没有参数的构造函数。当你在你的类上调用它时,它的构造函数确实有参数,这会导致运行时RTTI异常。
据我了解的代码,类实例化仅用于截取其虚拟方法的目的。我们不想在课堂上做任何其他事情,因为那样会打败嘲笑它的目的。您真正需要的是具有合适的vtable的对象的实例,可由TVirtualMethodInterceptor
操纵。你不需要或者不想让你的构造函数运行。你只是希望能够模拟恰好具有参数的构造函数的类。
因此,而不是调用构造函数的代码,我建议您修改它以使其调用NewInstance
。这是为了拥有一个可以被操纵的vtable所需要做的最低限度的事情。而且您还需要修改代码,以便它不会试图销毁模拟实例,而是调用FreeInstance
。只要你所做的就是在模拟上调用虚拟方法,所有这些都可以正常工作。
的修改是这样的:
constructor TObjectProxy<T>.Create;
var
ctx : TRttiContext;
rType : TRttiType;
NewInstance : TRttiMethod;
instance : TValue;
begin
inherited;
ctx := TRttiContext.Create;
rType := ctx.GetType(TypeInfo(T));
if rType = nil then
raise EMockNoRTTIException.Create('No TypeInfo found for T');
NewInstance := rType.GetMethod('NewInstance');
if NewInstance = nil then
raise EMockException.Create('Could not find NewInstance method on type ' + rType.Name);
instance := NewInstance.Invoke(rType.AsInstance.MetaclassType, []);
FInstance := instance.AsType<T>();
FVMInterceptor := TVirtualMethodInterceptor.Create(rType.AsInstance.MetaclassType);
FVMInterceptor.Proxify(instance.AsObject);
FVMInterceptor.OnBefore := DoBefore;
end;
destructor TObjectProxy<T>.Destroy;
begin
TObject(Pointer(@FInstance)^).FreeInstance;//always dispose of the instance before the interceptor.
FVMInterceptor.Free;
inherited;
end;
坦率地说,这看起来更明智一点给我。调用构造函数和析构函数肯定没有意义。
请让我知道,如果我在这里广泛的标志,并错过了这一点。这完全有可能!
这是我不明白。如果你想模拟一个类,为什么你想要创建一个你正在嘲笑的类的实例。当然,嘲笑的全部意义在于,你嘲笑课堂。 – 2013-03-23 14:18:46
当您执行'TMock。创建'Mocks框架将创建'TFoo'的实例。也许我不明白嘲笑,但我认为整个观点是你创造了一些不是'TFoo'的东西。我的意思是,如果你需要做的就是创建'TFoo',那么就做吧。如果你想嘲笑它,然后找到一个框架,将创建一个'TFoo'的模拟,而不是'TFoo'的一个实例。 –
2013-03-23 14:47:09
@David。我很抱歉,我的问题跳到我的问题没有任何背景;你是对的。我想模拟一个构造函数有一个参数的类。正如在Delphi-Mocks项目展示[TesTObjectMock示例](https://github.com/VSoftTechnologies/Delphi-Mocks/blob/master/Sample1Main)上提供的示例一样。pas)被测试的类(TFoo)作为通用参数传递,如同mock:= TMock .create。问题在于类函数“Create”,它调用“Invoke”。 –
TDF
2013-03-23 14:53:52