Delphi模拟-是否可以在用“WillReturn”模拟的函数中使用“VAR”或“OUT”安排



我刚刚开始在我的dunit测试中使用Delphi Mocks,但它几乎没有文档。

问题是:

我正在尝试编写一个测试'test_LogoUser_CheckPwd_GOOD_PASSWORD'

但我不知道如何模拟这个函数保险丝.CheckPwd(TEST_USERID、TEST_PASSWORD、ERROR_CODE)

通常我会使用:Fusers.Setup.WillReturn(True).Whin.CheckPwd(TEST_USERID、TEST_PASSWORD、ERROR_CODE)

但是"ERROR_CODE"是一个OUT值,并给出一个编译错误

问题:

  1. 有没有办法让"WillReturn"与OUT或VAR参数一起工作
  2. 是否有不同的方法模拟"CheckPwd",以便查看out或VAR参数

这是我的代码:

/////  TDlUsers
interface
type
{$M+}
  IDlUsers = Interface(IInterface)
 ['{3611B437-888C-4919-B304-238A80DAD476}']
    function   VerifyPassword(UserId: integer; Pwd: string): Boolean;
    function   CheckPwd(UserId: integer; Pwd: string; out ErrorCode: Integer) : Boolean;
  end;
  function  Dl_Users : IDlUsers;
{$M-}
implementation
type
  TDlUsers = class(TDbIControl,IDlUsers)
  public
    function   VerifyPassword(UserId: integer; Pwd: string): Boolean;
    function   CheckPwd(UserId: integer; Pwd: string; out ErrorCode: Integer; out ErrorMsg:String) : Boolean;
  end;
  function  Dl_Users : IDlUsers;
  begin
    result :=  TDlUsers.create;
  end;
////  TUserCtrl
interface
uses DlUsers;
type
  TUserCtrl = class
  private
    FUsers      : IDlUsers;
  public
    function    LogonUser_CheckPwd(UserID: Integer; Pwd: String): Boolean;
    function    LogonUser_VerifyPassword(UserID: Integer; Pwd: String): Boolean;
    constructor Create;  overload;
    constructor Create(FUsers : IDlUsers); overload;  { used for Dependency injection }
  end;
implementation
constructor TUserCtrl.Create;
begin
  Create(Dl_Users);
end;
constructor TUserCtrl.Create(FUsers : IDlUsers);
begin
  Self.FUsers          := FUsers;
end;
function TUserCtrl.LogonUser_VerifyPassword(UserID: Integer; Pwd: String): Boolean;
begin
   result := FUsers.VerifyPassword(UserId,Pwd);
end 
function  TUserCtrl.LogonUser_CheckPwd(UserID: Integer; Pwd: String): Boolean;
var ErrorCode : Integer; 
begin
   result := FUsers.CheckPwd(UserID,Pwd,ErrorCode);
   // do what needs to be done with ErrorCode
end;
///// Unit tests

procedure TestTDlUsers.SetUp;
begin
  inherited;
  FUsers    := TMock<IDlUsers>.Create;
  FUserCtrl := TUserCtrl.Create(FUsers);
end;
procedure TestTDlUsers.Test_LogonUser_VerifyPassword_GOOD_PASSWORD;
var Answer : Boolean;
begin
  FUsers.Setup.WillReturnDefault('VerifyPassword',False);
  FUsers.Setup.WillReturn(True).When.VerifyPassword(TEST_USERID,TEST_PASSWORD);
  Answer := FUserCtrl.LogonUser_VerifyPassword(TEST_USERID,TEST_PASSWORD,ErrorCode,ErrorMsg);
  CheckEquals(True,Answer);
end;

procedure TestTDlUsers.Test_LogonUser_CheckPwd_GOOD_PASSWORD;
var Answer : Boolean;
begin
  FUsers.Setup.WillReturnDefault('CheckPwd',False);
  // MAJOR Problem with line  CheckPwd  has an Out pramater 
  FUsers.Setup.WillReturn(True).When.CheckPwd(TEST_USERID,TEST_PASSWORD, ERROR_CODE);

  Answer := FUserCtrl.LogonUser_CheckPwd(TEST_USERID,TEST_PASSWORD,ErrorCode,ErrorMsg);
  CheckEquals(True,Answer);
end;

AFAIK这是Delphi mocks所依赖的TVirtualInterface标准类实现的限制。这是"新RTTI"的弱点/局限性之一。

唯一可能的解决方案是使用一个不使用这个TVirtualInterface类的存根/嘲笑库。

我所知道的唯一一个有自己"虚拟类"工厂的库是我们的开源mORMot框架(适用于Delphi 6到XE4,在Win32和Win64下)。它支持varout值参数。为了测试任何输出参数值,您可以使用ExpectsTrace()方法,这要归功于mORMot的强大"调用跟踪"功能。

新的delphi mock可以通过引用函数WillExecute:来实现这一点

FUsers.Setup.WillExecute('CheckPwd',
  // function CheckPwd(UserId: integer; Pwd: string; out ErrorCode: Integer; out ErrorMsg: String) : Boolean;
  function (const args : TArray<TValue>; const ReturnType : TRttiType) : TValue
  var
     aErrorCode: Integer;
     aErrorMsg: String;
     aResult: Boolean
  begin
    // check against 5, as arg[0] is tkInterface and the method parameters start with arg[1]
    Assert.AreEqual(5, Length(args), 'wrong number of arguments passed to IDlUsers.CheckPwd(UserId: integer; Pwd: string; out ErrorCode: Integer; out ErrorMsg:String) : Boolean');
    Assert.IsTrue(args[1].IsType<integer>, 'wrong argument 1 type passed to IDlUsers.CheckPwd(UserId: integer; Pwd: string; out ErrorCode: Integer; out ErrorMsg:String) : Boolean');
    Assert.IsTrue(args[2].IsType<string>, 'wrong argument 2 type passed to IDlUsers.CheckPwd(UserId: integer; Pwd: string; out ErrorCode: Integer; out ErrorMsg:String) : Boolean');
    Assert.IsTrue(args[3].IsType<Integer>, 'wrong argument 3 type passed to IDlUsers.CheckPwd(UserId: integer; Pwd: string; out ErrorCode: Integer; out ErrorMsg:String) : Boolean');
    Assert.IsTrue(args[4].IsType<string>, 'wrong argument 4 type passed to IDlUsers.CheckPwd(UserId: integer; Pwd: string; out ErrorCode: Integer; out ErrorMsg:String) : Boolean');
    // ...
    arg[3] := TValue.From<Integer>(aErrorCode);
    arg[4] := TValue.From<string>(aErrorMsg);
    Result := TValue.From<Boolean>(aResult);
  end);

(我使用Delphi XE7和DUnitX)

您使用的库不考虑通过引用传递的参数。它将所有参数视为要在调用mock时匹配的输入值。此外,当调用模拟函数时,它无法为这些参数分配新值。

另一种选择是更改函数的接口,使其可测试。失败时可以抛出异常。然而,由于未能输入正确的用户名和密码并不是一个例外事件,因此异常并不适用于此函数。相反,请考虑返回错误代码。保留一个代码(通常为零)表示成功。

最新更新