I have just started to use Delphi-Mocks with my dunit tests but it has little or no documentation.
The Problem is:
I am trying to write a test ‘Test_LogonUser_CheckPwd_GOOD_PASSWORD’
But I am not sure how to mock out the function Fusers.CheckPwd(TEST_USERID,TEST_PASSWORD, ERROR_CODE);
Normally I would use: Fusers.Setup.WillReturn(True).When.CheckPwd(TEST_USERID,TEST_PASSWORD, ERROR_CODE);
But ‘ERROR_CODE’ is an OUT value and gives a compile error
QUESTIONS:
Here is my code:
///// 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 this is a limitation of the TVirtualInterface
standard class implementation, on which Delphi mocks relies. This is one of the weaknesses/limitations of the "new RTTI".
The only possible solution is to use a stubbing/mocking library which does not use this TVirtualInterface
class.
The only library I know which has its own "virtual class" factory, is our Open Source mORMot framework (for Delphi 6 up to XE4, under Win32 and Win64). It supports var
and out
value parameters. For testing any out parameter value, you can use the ExpectsTrace()
method - thanks to the great "Call Tracing" feature of mORMot.
New delphi-mock can do this using 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);
(I use Delphi XE7 and DUnitX)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With