2

我目前正在开始进行单元测试和模拟,我偶然发现了以下方法,我似乎无法为其制造一个有效的模拟实现:

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;

但我无法弄清楚我应该如何模拟 open array parameter AArgs。有任何想法吗?

此外,是否有一种更简单的方法来返回out-parameterAInstance并使用@-notation 作为TGUID-typed 参数(本质上是记录,即值类型)的正确方法?

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


更新 2:为了清楚起见,我现在删减了问题文本。最初它包含以下模拟方法的错误实现,这是梅森的回复所指的:

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])说“变量类型数组构造函数中的参数类型错误。”。

4

2 回答 2

0

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

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

于 2010-05-20T15:28:56.770 回答
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- 方法。我编写了几个实用程序,允许我将显式参数与开放数组参数合并到一个新数组中。

这里是实现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 如何在内部处理动态和开放数组有更深入了解的人进行大规模优化。

无论如何,在测试站点使用此解决方案后,我现在不得不忽略模拟方法中甚至还有一个开放数组参数的事实。我只是这样指定期望:

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

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

于 2010-06-11T11:33:27.320 回答