gpt4 book ai didi

Delphi-Mocks:在构造函数中使用参数模拟类

转载 作者:行者123 更新时间:2023-12-03 14:41:50 25 4
gpt4 key购买 nike

我开始使用 Delphi-Mocks框架,并且在模拟构造函数中具有参数的类时遇到问题。 TMock 的类函数“Create”不允许参数。如果尝试创建 TFoo.Create( Bar: someType ); 的模拟实例当 TObjectProxy.Create; 时,我收到参数计数不匹配的信息;尝试调用 T 的“Create”方法。

显然这是因为以下代码没有向“Invoke”方法传递任何参数:

instance := ctor.Invoke(rType.AsInstance.MetaclassType, []);

我创建了一个重载类函数,它确实传递参数:

class function Create( Args: array of TValue ): TMock<T>; overload;static;

并且正在进行我已经完成的有限测试。

我的问题是:

这是一个错误还是我只是做错了?

谢谢

PS:我知道 Delphi-Mocks 是以接口(interface)为中心的,但它确实支持类,并且我正在处理的代码库 99% 是类。

最佳答案

在我看来,根本问题是 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 异常。

据我了解代码,实例化该类只是为了拦截其虚拟方法。我们不想对这个类做任何其他事情,因为这会破坏 mock 它的目的。您真正需要的是一个具有合适 vtable 的对象实例,可以通过 TVirtualMethodInterceptor 进行操作。 。您不需要或不希望您的构造函数运行。您只是希望能够模拟一个恰好有一个带参数的构造函数的类。

因此,我建议您修改它以使其调用 NewInstance,而不是调用构造函数。 。这是获得可操作的虚函数表所需要做的最低限度的事情。您还需要修改代码,以便它不会尝试销毁模拟实例,而是调用 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;

坦白说,这对我来说看起来更明智一些。调用构造函数和析构函数肯定没有意义。

如果我在这里偏离主题并且没有捕获重点,请告诉我。这完全有可能!

关于Delphi-Mocks:在构造函数中使用参数模拟类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15582277/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com