Delphi 如何在PascalMock中模拟具有开放数组参数的方法?

Delphi 如何在PascalMock中模拟具有开放数组参数的方法?,delphi,unit-testing,mocking,parameter-passing,pascalmock,Delphi,Unit Testing,Mocking,Parameter Passing,Pascalmock,我目前正在着手进行单元测试和模拟,我无意中发现了以下方法,我似乎无法为其制作一个工作模拟实现: function GetInstance(const AIID: TGUID; out AInstance; const AArgs: array of const; const AContextID: TImplContextID = CID_DEFAULT): B

我目前正在着手进行单元测试和模拟,我无意中发现了以下方法,我似乎无法为其制作一个工作模拟实现:

function GetInstance(const AIID: TGUID; 
                       out AInstance; 
                     const AArgs: array of const; 
                     const AContextID: TImplContextID = CID_DEFAULT): Boolean;
TImplContextID
只是整数的类型别名)

这就是我取得的成绩:

function TImplementationProviderMock.GetInstance(
  const AIID: TGUID;
    out AInstance;
  const AArgs: array of const;
  const AContextID: TImplContextID): Boolean;
var
  lCall: TMockMethod;
begin
  lCall := AddCall('GetInstance').WithParams([@AIID, AContextID]);
  Pointer(AInstance) := FindVarData(lCall.OutParams[0]).VPointer;
  Result := lCall.ReturnValue;
end;
但是我还没有弄清楚我应该如何模拟开放数组参数
AArgs
。有什么想法吗

此外,是否有更简单的方法返回
out
-参数
AInstance
,并使用
@
-符号表示
TGUID
-类型的参数(本质上是记录,即值类型)是正确的方法

是否可以用当前版本的PascalMock模拟此方法


更新2:为了清晰起见,我现在已经删去了问题文本。最初,它包含以下错误的模拟方法实现,这正是Mason的答复所指:

function TImplementationProviderMock.GetInstance(
  const AIID: TGUID;
    out AInstance;
  const AArgs: array of const;
  const AContextID: TImplContextID): Boolean;
begin
  Result := AddCall('GetInstance')
           .WithParams([@AIID, AContextID])
           .ReturnsOutParams([AInstance])
           .ReturnValue;
end;

在这种情况下,编译器抱怨
.returnsoutpams([AInstance])
说“变量类型数组构造函数中的参数类型不正确”。

看起来
returnsoutpams
需要一个const数组,它在内部作为TVarRec数组实现。TVarRec是一个类似于变体但不同的记录,它需要编译器定义一个类型来填充它。非类型化参数不会进入其中


这类事情可能可以通过Delphi 2010的扩展RTTI实现,但不能通过TVarRec实现。

看起来
ReturnsOutParams
需要一个常量数组,它在内部作为TVarRec数组实现。TVarRec是一个类似于变体但不同的记录,它需要编译器定义一个类型来填充它。非类型化参数不会进入其中


这类事情可能可以用Delphi 2010的扩展RTTI来完成,但不能用TVarRec来完成。

我现在提出了一个有点复杂的解决方案,从OO- POV,因为它要求测试的实现者知道模拟是如何在内部实现的,但我认为这在某种程度上仍然是可以接受的,因为无论如何都不能假设如何指定一个开放数组参数期望(至少不会编译)

这就是我的模拟方法的实现现在的样子:

function TImplementationProviderMock.GetInstance(
  const AIID: TGUID;
    out AInstance;
  const AArgs: array of const;
  const AContextID: TImplContextID): Boolean;
var
  lCall: TMockMethod;
  lArgs: TOpenArray;
begin
  lArgs := ConcatArrays([ArgsToArray([@AIID]), ArgsToArray(AArgs), ArgsToArray([AContextID])]);
  lCall := AddCall('GetInstance').WithParams(lArgs);
  Pointer(AInstance) := FindVarData(lCall.OutParams[0]).VPointer;
  Result := lCall.ReturnValue;
end;
正如您所见,我的解决方案的核心是构建我自己的TVarRec
数组(也称为
TOpenArray
),然后我可以将其传递给
with params
-方法。我编写了两个实用程序例程,允许我将显式参数与开放数组参数合并到一个新数组中

下面是
concatarray
的实现:

type TOpenArray = array of TVarRec;

function ConcatArrays(const AArrays: array of TOpenArray): TOpenArray;
var
  lLength: Integer;
  lArray: TOpenArray;
  lIdx: Integer;
  lElem: TVarRec;
begin
  lLength := 0;
  for lArray in AArrays do
    Inc(lLength, Length(lArray));
  SetLength(Result, lLength);
  lIdx := -1;
  for lArray in AArrays do
    for lElem in lArray do
      begin
        Inc(lIdx);
        Result[lIdx] := lElem;
      end;
end;
我非常怀疑,如果有人对Delphi如何在内部处理动态和开放数组有更深入的了解,这些例程可能会得到大规模优化

无论如何,在测试站点上有了这个解决方案后,我现在不得不忽略一个事实,即在模拟方法中甚至有一个开放数组参数。我只是简单地指定期望值:

FMock.Expects('GetInstance').WithParams([@IMyIntf, 1, 2, 3, lContextID]).ReturnsOutParam(lDummy).Returns(True);

。。。其中,
1,2,3
-位实际上是预期的开放数组参数。

我现在提出了一个有点复杂的解决方案,从OO- POV,因为它要求测试的实现者知道模拟是如何在内部实现的,但我认为这在某种程度上仍然是可以接受的,因为无论如何都不能假设如何指定一个开放数组参数期望(至少不会编译)

这就是我的模拟方法的实现现在的样子:

function TImplementationProviderMock.GetInstance(
  const AIID: TGUID;
    out AInstance;
  const AArgs: array of const;
  const AContextID: TImplContextID): Boolean;
var
  lCall: TMockMethod;
  lArgs: TOpenArray;
begin
  lArgs := ConcatArrays([ArgsToArray([@AIID]), ArgsToArray(AArgs), ArgsToArray([AContextID])]);
  lCall := AddCall('GetInstance').WithParams(lArgs);
  Pointer(AInstance) := FindVarData(lCall.OutParams[0]).VPointer;
  Result := lCall.ReturnValue;
end;
正如您所见,我的解决方案的核心是构建我自己的TVarRec
数组(也称为
TOpenArray
),然后我可以将其传递给
with params
-方法。我编写了两个实用程序例程,允许我将显式参数与开放数组参数合并到一个新数组中

下面是
concatarray
的实现:

type TOpenArray = array of TVarRec;

function ConcatArrays(const AArrays: array of TOpenArray): TOpenArray;
var
  lLength: Integer;
  lArray: TOpenArray;
  lIdx: Integer;
  lElem: TVarRec;
begin
  lLength := 0;
  for lArray in AArrays do
    Inc(lLength, Length(lArray));
  SetLength(Result, lLength);
  lIdx := -1;
  for lArray in AArrays do
    for lElem in lArray do
      begin
        Inc(lIdx);
        Result[lIdx] := lElem;
      end;
end;
我非常怀疑,如果有人对Delphi如何在内部处理动态和开放数组有更深入的了解,这些例程可能会得到大规模优化

无论如何,在测试站点上有了这个解决方案后,我现在不得不忽略一个事实,即在模拟方法中甚至有一个开放数组参数。我只是简单地指定期望值:

FMock.Expects('GetInstance').WithParams([@IMyIntf, 1, 2, 3, lContextID]).ReturnsOutParam(lDummy).Returns(True);

。。。其中
1,2,3
-位实际上是预期的开放数组参数。

我现在非常确定在定义预期时应该调用
ReturnsOutParams
,而不是模拟调用。否则就没什么意义了
tLockMethod
还有一个名为
OutParams
的数组属性,我认为应该使用它来返回以前通过
ReturnsOutParams
填充的值@Oliver:同样,我认为你不能用TVarRec来实现这一点。我不是PascalMock专家,但在我看来,这个函数对于它提供的接口来说太复杂了。我现在非常确定在定义期望时应该调用
ReturnsOutParams
,而不是模拟调用。否则就没什么意义了
tLockMethod
还有一个名为
OutParams
的数组属性,我认为应该使用它来返回以前通过
ReturnsOutParams
填充的值@Oliver:同样,我认为你不能用TVarRec来实现这一点。我不是PascalMock专家,但在我看来,这个函数对于它提供的接口来说太复杂了。如果你用@AArgs添加AArgs,你可以编译它,但是因为我不使用PascalMock,所以我不确定这是否会产生所需的行为。如果你愿意,你可以编译它