Delphi Mocks - Можно ли использовать механизмы "VAR" или "OUT" в функции, которая была смоделирована с помощью "WillReturn"?

Я только начал использовать 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?

Вот мой код:

/////  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 ответа

Решение

AFAIK это ограничение TVirtualInterface стандартная реализация класса, на которую опирается Delphi. Это одна из слабостей / ограничений "нового RTTI".

Единственное возможное решение - использовать библиотеку-заглушку, которая этого не использует. TVirtualInterface учебный класс.

Единственная из известных мне библиотек, которая имеет собственную фабрику "виртуальных классов", - это наша платформа с открытым исходным кодом mORMot (для Delphi 6 до XE4, под Win32 и Win64). Поддерживает var а также out значения параметров. Для проверки любого значения параметра, вы можете использовать ExpectsTrace() метод - благодаря замечательной функции "Call Tracing" в 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)

Используемая вами библиотека не учитывает параметры, передаваемые по ссылке. Он обрабатывает все параметры как входные значения для сопоставления при вызове макета. Кроме того, он не может назначать новые значения таким параметрам при вызове фиктивной функции.

В качестве альтернативы вы можете изменить интерфейс своей функции, чтобы сделать ее тестируемой. Вы можете бросить исключение в случае неудачи. Однако исключения не совсем подходят для этой функции, поскольку невозможность ввести правильное имя пользователя и пароль не является исключительным событием. Вместо этого рассмотрите возможность возврата кода ошибки. Зарезервируйте один код (обычно ноль), чтобы указать успех.

Другие вопросы по тегам