2013-08-16 17 views
5

Ho appena iniziato a utilizzare Delphi-Mocks con i miei test di dunit ma ha poca o nessuna documentazione.Delphi Mock - È possibile utilizzare gli arrangiamenti 'VAR' o 'OUT' in una funzione che è stata derisa con un 'WillReturn'?

Il problema è:

Sto cercando di scrivere un test 'Test_LogonUser_CheckPwd_GOOD_PASSWORD'

Ma io non sono sicuro di come per deridere la funzione Fusers.CheckPwd (TEST_USERID, TEST_PASSWORD, ERROR_CODE);

Normalmente avrei usato: Fusers.Setup.WillReturn (True) .When.CheckPwd (TEST_USERID, TEST_PASSWORD, ERROR_CODE);

Ma ‘ERROR_CODE’ è un valore OUT e dà un errore di compilazione

DOMANDE:

  1. C'è un modo per far funzionare ‘willreturn’ con parametri OUT o VAR?
  2. C'è un modo diverso di prendere in giro "CheckPwd" in modo che guardi i parametri OUT o VAR?

Ecco il mio codice:

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

risposta

4

AFAIK questa è una limitazione dell'implementazione della classe standard TVirtualInterface, sulla quale si basa Dockshi Docks. Questo è uno dei punti deboli/limiti del "nuovo RTTI".

L'unica soluzione possibile è utilizzare una libreria di stub/di derisione che non utilizza questa classe TVirtualInterface.

L'unica libreria che conosco che ha una propria fabbrica di "classe virtuale" è our Open Source mORMot framework (per Delphi 6 fino a XE4, in Win32 e Win64). Supporta i parametri di valore var e out. Per testare qualsiasi valore del parametro, è possibile utilizzare il metodo ExpectsTrace(), grazie all'ottimo "Call Tracing" feature of mORMot.

2

La biblioteca si sta utilizzando dà alcuna considerazione per i parametri passati per riferimento. Tratta tutti i parametri come valori di input da abbinare su una chiamata alla simulazione. Inoltre, non ha modo di assegnare nuovi valori a tali parametri quando viene chiamata la funzione fittizia.

Un'alternativa è per voi per cambiare l'interfaccia per la funzione per renderla testabile. È possibile generare un'eccezione in caso di errore. Tuttavia, le eccezioni non sono realmente appropriate per questa funzione poiché l'errore di inserire il nome utente e la password corretti non è un evento eccezionale. Invece, considera di restituire il codice di errore. Prenota un codice (tipicamente zero) per indicare il successo.

+2

Cambiare l'interfaccia causa di una limitazione di terze parti (che è in effetti una limitazione di A/R di Delphi RTL)? Ci sono molti casi in cui i parametri 'var' e' out' sono la soluzione migliore per la definizione del metodo. Oppure potresti dover usare una classe o un record come tipo di risultato "tupple" ... Non sono convinto che sia più "pascalish". –

2

Nuova Delphi-finto può fare questo con riferimento a funzione-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); 

(io uso Delphi XE7 e DUnitX)

+0

Grazie mille! Questa risposta mi ha appena salvato !! –

+0

Interessante avrebbe bisogno di più contesto su come usare questo –