如何在PascalMock中使用open数组参数模拟方法?

时间:2010-05-20 14:30:24

标签: delphi unit-testing mocking parameter-passing pascalmock

我目前正处于开始进行单元测试和嘲笑的过程中,我偶然发现了以下方法,我似乎无法为以下方法构建一个有效的模拟实现:

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;

在这里,编译器抱怨.ReturnsOutParams([AInstance])说“变量类型数组构造函数中的错误参数类型。”。

2 个答案:

答案 0 :(得分:0)

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

这种事情可能是通过Delphi 2010的扩展RTTI完成的,但不能用TVarRec完成。

答案 1 :(得分:0)

我现在想出了一个有点精心设计的解决方案,这个解决方案并不是完全理想的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;

正如您所看到的,我的解决方案的核心是构建我自己的array of TVarRec(又名TOpenArray),然后我可以将其传递给WithParams - 方法。我编写了几个实用程序例程,允许我将显式参数与open数组参数合并为一个新数组。

以下是ConcatArrays的实施:

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如何在内部处理动态和开放数组的人大量优化。

无论如何,在测试站点使用此解决方案时,我现在必须忽略这样一个事实,即在mocked方法中甚至有一个打开的数组参数。我只是指定期望:

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

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