在我看来,根本问题是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;
如您所见,代码假设您的类具有无参数构造函数。当你在你的类上调用 this 时,它的构造函数确实有参数,这会导致运行时 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;
坦率地说,这对我来说看起来更明智。调用构造函数和析构函数肯定没有意义。
请让我知道我是否在这里偏离了主题并且错过了重点。这是完全可能的!