2013-08-16 21 views
5

Właśnie zacząłem używać Delphi-Mocks z moimi testami dunit, ale ma to małą lub żadną dokumentację.Delphi Mocks - Czy można użyć funkcji "VAR" lub "OUT" w funkcji, która została wyśmiana przez "WillReturn"?

Problem jest:

Próbuję napisać test 'Test_LogonUser_CheckPwd_GOOD_PASSWORD'

Ale nie jestem pewien, jak wyśmiewać się funkcję Fusers.CheckPwd (TEST_USERID, TEST_PASSWORD, ERROR_CODE);

Normalnie używam: Fusers.Setup.WillReturn (True) .When.CheckPwd (TEST_USERID, TEST_PASSWORD, ERROR_CODE);

Ale „ERROR_CODE” jest wartością OUT i daje błąd kompilacji

PYTANIA:

  1. Czy istnieje sposób, aby uczynić pracę „WillReturn” z parametrami OUT lub VAR?
  2. Czy istnieje inny sposób na wyodrębnienie "CheckPwd" tak, aby wyglądał na parametry OUT lub VAR?

Oto mój kod:

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

Odpowiedz

4

AFAIK jest to ograniczenie standardowej implementacji klasy TVirtualInterface, na której polega hiperłącza Delphi. Jest to jedna ze słabości/ograniczeń "nowego RTTI".

Jedynym możliwym rozwiązaniem jest użycie biblioteki kodującej, która nie używa tej klasy.

Jedyną biblioteką, którą znam, która ma własną fabrykę "wirtualnej klasy", jest our Open Source mORMot framework (dla Delphi 6 do XE4, pod Win32 i Win64). Obsługuje parametry wartości var i . Aby przetestować dowolną wartość parametru wyjściowego, można użyć metody ExpectsTrace() - dzięki wspaniałemu "Call Tracing" feature of mORMot.

2

Biblioteka używasz nie daje żadnych względów dla parametrów przekazywanych przez odniesienie. Traktuje wszystkie parametry jako wartości wejściowe, które mają zostać dopasowane podczas połączenia z próbą. Ponadto nie ma możliwości przypisania nowych wartości do tych parametrów, gdy wywoływana jest funkcja wyśmiewana.

Alternatywą jest zmiana interfejsu funkcji w celu jej przetestowania. Możesz rzucić wyjątek na porażkę. Jednak wyjątki nie są odpowiednie dla tej funkcji, ponieważ niepowodzenie w wpisaniu poprawnej nazwy użytkownika i hasła nie jest zdarzeniem wyjątkowym. Zamiast tego rozważ powrót do kodu błędu. Zarezerwuj jeden kod (zwykle zero), aby wskazać powodzenie.

+2

Zmiana interfejsu powodu ograniczeń trzeciej (która jest w rzeczywistości ograniczeniem RTL Delphi AFAIK)? Istnieje wiele przypadków, w których parametry "var" i "out" są najlepszym rozwiązaniem do definicji metody. Albo może będziesz musiał użyć klasy lub nagrać jako wynik "tupple" ... Nie jestem przekonany, że jest bardziej "pascalish". –

2

New Delphi-makiety można zrobić przy użyciu reference-to-funkcja-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); 

(używam Delphi XE7 i DUnitX)

+0

Dziękuję bardzo! Ta odpowiedź mnie uratowała! –

+0

Interesujące byłoby więcej kontekstu, jak tego używać –