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 и выдает ошибку компиляции
ВОПРОСЫ:
- Есть ли способ заставить 'WillReturn' работать с параметрами OUT или VAR?
- Есть ли другой способ смоделировать 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)
Используемая вами библиотека не учитывает параметры, передаваемые по ссылке. Он обрабатывает все параметры как входные значения для сопоставления при вызове макета. Кроме того, он не может назначать новые значения таким параметрам при вызове фиктивной функции.
В качестве альтернативы вы можете изменить интерфейс своей функции, чтобы сделать ее тестируемой. Вы можете бросить исключение в случае неудачи. Однако исключения не совсем подходят для этой функции, поскольку невозможность ввести правильное имя пользователя и пароль не является исключительным событием. Вместо этого рассмотрите возможность возврата кода ошибки. Зарезервируйте один код (обычно ноль), чтобы указать успех.