德尔福模拟 - 是否可以在使用'WillReturn'模拟的函数中使用'VAR'或'OUT'排列?

时间:2013-08-16 11:50:07

标签: delphi delphi-xe2 dunit delphi-mocks

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

问题是:

我正在尝试编写测试'Test_LogonUser_CheckPwd_GOOD_PASSWORD'

但我不知道如何模拟这个功能       Fusers.CheckPwd(TEST_USERID,TEST_PASSWORD,ERROR_CODE);

通常我会用:      Fusers.Setup.WillReturn(True).When.CheckPwd(TEST_USERID,TEST_PASSWORD,ERROR_CODE);

但是'ERROR_CODE'是OUT值并且给出了编译错误

问题:

  1. 有没有办法让'WillReturn'能够使用OUT或VAR参数?
  2. 有没有不同的方法来模拟'CheckPwd',以便它查看OUT或VAR参数?
  3. 这是我的代码:

    /////  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;
    

3 个答案:

答案 0 :(得分:4)

AFAIK这是Delphi模拟所依赖的TVirtualInterface标准类实现的限制。这是“新RTTI”的弱点/局限之一。

唯一可行的解​​决方案是使用不使用此TVirtualInterface类的存根/模拟库。

我所知道的唯一拥有自己的“虚拟类”工厂的库是our Open Source mORMot framework(对于Delphi 6到XE4,在Win32和Win64下)。它支持varout值参数。要测试任何输出参数值,您可以使用ExpectsTrace()方法 - 感谢伟大的"Call Tracing" feature of mORMot

答案 1 :(得分:3)

新的delphi-mock可以使用reference-to-function-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)

答案 2 :(得分:2)

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

另一种方法是更改​​函数的界面以使其可测试。您可以在失败时抛出异常。但是,异常并不适合此功能,因为输入正确的用户名和密码不是特殊事件。相反,请考虑返回错误代码。保留一个代码(通常为零)以表示成功。