Delphi Mocks:在构造函数中使用参数模拟类
我开始使用这个框架,但在模拟构造函数中有参数的类时遇到了麻烦。TMock的类函数“Create”不允许使用参数。如果尝试创建TFoo.create(Bar:someType)的模拟实例;我在TObjectProxy.Create时得到一个参数计数不匹配;尝试调用T的“Create”方法 显然,这是因为以下代码没有向“Invoke”方法传递任何参数: 我创建了一个重载类函数,该函数传递参数:Delphi Mocks:在构造函数中使用参数模拟类,delphi,mocking,delphi-mocks,Delphi,Mocking,Delphi Mocks,我开始使用这个框架,但在模拟构造函数中有参数的类时遇到了麻烦。TMock的类函数“Create”不允许使用参数。如果尝试创建TFoo.create(Bar:someType)的模拟实例;我在TObjectProxy.Create时得到一个参数计数不匹配;尝试调用T的“Create”方法 显然,这是因为以下代码没有向“Invoke”方法传递任何参数: 我创建了一个重载类函数,该函数传递参数: class function Create( Args: array of TValue ): TMock
class function Create( Args: array of TValue ): TMock<T>; overload;static;
class函数Create(Args:TValue数组):TMock;超载;静止的
并且正在进行我所做的有限测试
我的问题是:
这是一个错误还是我做错了
谢谢
PS:我知道Delphi Mocks是以接口为中心的,但它确实支持类,而且我正在处理的代码库中99%是类。免责声明:我对Delphi Mocks一无所知 我想这是故意的。从您的示例代码来看,Delphi Mocks似乎在使用泛型。如果要实例化泛型参数的实例,如中所示:
function TSomeClass<T>.CreateType: T;
begin
Result := T.Create;
end;
函数TSomeClass.CreateType:T;
开始
结果:=T.Create;
结束;
然后您需要泛型类上的构造函数约束:
TSomeClass<T: class, constructor> = class
TSomeClass=class
具有构造函数约束意味着传入的类型必须具有无参数构造函数
你可能会做类似的事情
TSomeClass<T: TSomeBaseMockableClass, constructor> = class
TSomeClass=class
并给TSomeBaseMockableClass
一个可以使用的特定构造函数,但是:
要求框架的所有用户从特定的基类派生出他们的所有类只是。。。好。。。过度限制(说得温和一点),尤其是考虑到Delphi的单一继承。在我看来,基本问题是
TMock.Create
导致被测试的类(CUT)被实例化。我怀疑该框架是在假设您将模拟抽象基类的情况下设计的。在这种情况下,实例化它将是良性的。我怀疑您正在处理的遗留代码没有方便的抽象基类。但在您的例子中,实例化CUT的唯一方法是将参数传递给构造函数,因此无法实现模拟的全部目的。我认为重新设计遗留代码库需要做大量的工作,直到为所有需要模拟的类建立一个抽象基类
您正在编写TMock.Create
,其中TFoo
是一个类。这将导致创建代理对象。这发生在TObjectProxy.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;
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;
坦白地说,我觉得这更合理一些。调用构造函数和析构函数毫无意义
请让我知道,如果我在这里偏离了目标,没有抓住要点。那完全有可能 我不确定我是否正确地理解了您的需求,但这种黑客方法可能会有所帮助。假设您有一个类在其构造函数中需要一个参数
type
TMyClass = class
public
constructor Create(AValue: Integer);
end;
可以使用无参数构造函数和保存参数的类属性继承此类
type
TMyClassMockable = class(TMyClass)
private
class var
FACreateParam: Integer;
public
constructor Create;
class property ACreateParam: Integer read FACreateParam write FACreateParam;
end;
constructor TMyClassMockable.Create;
begin
inherited Create(ACreateParam);
end;
现在可以使用class属性将参数传递给构造函数。当然,您必须将继承的类赋予mock框架,但是由于没有其他改变,派生类也应该这样做
只有当您确切地知道类何时被实例化,以便能够为类属性提供适当的参数时,这才起作用
不用说,这种方法不是线程安全的。它不是泛型。代码使用RTTI调用构造函数。它假定它的名称为
Create
。如果代码需要,它可以在调用TRttiMethod
实例上的Invoke
时非常轻松地传递参数。@DavidHeffernan-Aha。但即使不是泛型,框架如何知道要传递哪些参数和值?Rtti可以确定参数的数量和类型,但框架仍然不知道这些参数的含义。如果希望框架实例化类,则需要“泛型”构造函数:无参数构造函数和/或接收TValue(或Variant)数组的构造函数;或者,您必须在模拟框架上有一个通用方法,为其提供一个TValue数组,以便按规范顺序传递到构造函数的特定参数中。您只需将参数传递到TMock.Create
。TValue
的开放数组。但对我来说,我无法理解为什么要使用mock来创建真实类的实例。“这对我来说毫无意义。”大卫没有举例说明真正的类,而是举例说明它的模拟类。如果真实的类有构造函数参数,您可能需要某种方法来确保mock也可以使用它们。请注意,我不使用嘲弄(而是存根),所以我可能有点离谱。然而,两者都非常依赖依赖于依赖注入(不希望增加增强RTTI负担的框架也需要无参数构造函数)。如果您没有DI,测试c/下的类将创建您想要模拟/存根的实例。如果你有特定的构造器…@marjannema,谢谢。由于我使用的是“某种程度上”遗留的代码库,因此我确实有一些特定的类,这些类的构造函数需要参数。如果您试图模拟一个类,为什么要创建您要模拟的类的实例。毫无疑问,模拟的全部意义在于,你可以模拟这个类。当你做TMock.Create
时,模拟框架会创建TFoo
的一个实例。也许我不懂嘲弄,但我想重点是你创造了一些
type
TMyClassMockable = class(TMyClass)
private
class var
FACreateParam: Integer;
public
constructor Create;
class property ACreateParam: Integer read FACreateParam write FACreateParam;
end;
constructor TMyClassMockable.Create;
begin
inherited Create(ACreateParam);
end;