2011-02-05 7 views
12

Sto creando test di unità con DUnit. Ho una lezione che richiede molto tempo per inizializzarsi.Come accedere ai campi di un TTestCase in una classe TTestSetup

I deriva una classe TMyTestSetup da TTestSetup e sovrascrive il suo metodo di installazione. Questo metodo SetUp viene chiamato una sola volta per tutti i test nel mio TTestCase. Inserisco il processo di inizializzazione nella routine TMyTestSetup.SetUp per migliorare le prestazioni.

Il mio problema è come posso accedere all'oggetto che voglio inizializzare, che è un campo del mio TMyTest nella classe TestSetup? L'unico modo per farlo è dichiararlo globalmente?

non testati breve esempio:

TMyTestSetup = class(TTestSetup) 
    protected 
    procedure SetUp; override; 
end; 

TMyTest = class(TTestcase) 
public 
    fTakes4Ever2Init : TInits4Ever2Init; 
published 
    procedure Test1;  
end; 

implementation 

procedure TMyTestSetup.Setup; 
begin 
    // How can I access fTakes4Ever2Init from here? 
    fTakes4Ever2Init.create // This is the call that takes long 
end; 

procedure TMyTest.Test1; 
begin 
    fTakes4Ever2Init.DoSomething; 
end; 

initialization 
    RegisterTest(TMyTestSetup.Create(TMyTest.Suite)); 
+0

Penso che tu abbia il modo sbagliato. Penso che tu voglia accedere all'istanza 'TMyTestSetup' da' TMyTest', ma potrei sbagliarmi !!! –

+0

In entrambi i casi non ho idea di come realizzarlo –

+0

Devo ammettere che trovo DUnit un po 'opaco a volte! –

risposta

9

Il trucco consiste nell'utilizzare una variabile di classe pubblica nella classe TMyTestSetup.

Ti piace questa (testato e funzionante, completa) Esempio:

unit TestTestUnit; 

interface 

uses 
    TestFramework, TestExtensions; 

type 
    TInits4Ever2Init = class 
    private 
    FValue: integer; 
    public 
    constructor Create; 
    procedure DoSomething1; 
    procedure DoSomething2; 
    procedure DoSomething3; 
    end; 

type 
    TMyTestSetup = class(TTestSetup) 
    public class var 
    fTakes4Ever2Init: TInits4Ever2Init; 
    protected 
    procedure SetUp; override; 
    end; 

    TMyTest = class(TTestCase) 
    published 
    procedure Test1; 
    procedure Test2; 
    procedure Test3; 
    end; 

implementation 

uses 
    SysUtils, Windows; 

{ TMyTestSetup } 

procedure TMyTestSetup.Setup; 
begin 
    fTakes4Ever2Init := TInits4Ever2Init.create; // This is the call that takes long 
end; 

{ TMyTest } 

procedure TMyTest.Test1; 
begin 
    TMyTestSetup.fTakes4Ever2Init.DoSomething1; 
end; 

procedure TMyTest.Test2; 
begin 
    TMyTestSetup.fTakes4Ever2Init.DoSomething2; 
end; 

procedure TMyTest.Test3; 
begin 
    TMyTestSetup.fTakes4Ever2Init.DoSomething3; 
end; 

{ TInits4Ever2Init } 

constructor TInits4Ever2Init.Create; 
begin 
    inherited Create; 

    // FValue and Format('%p, %d', [Pointer(Self), FValue])) are to confirm 
    // that we are talking to the same object for all the tests, 
    // but that the object is different each time we run the test suite. 

    Randomize; 
    FValue := Random(10000); 

    OutputDebugString(pAnsiChar('-- TInits4Ever2Init.Create: ' 
    + Format('%p, %d', [Pointer(Self), FValue]))); 
end; 

procedure TInits4Ever2Init.DoSomething1; 
begin 
    OutputDebugString(pAnsiChar('-- TInits4Ever2Init.DoSomething1: ' 
    + Format('%p, %d', [Pointer(Self), FValue]))); 
end; 

procedure TInits4Ever2Init.DoSomething2; 
begin 
    OutputDebugString(pAnsiChar('-- TInits4Ever2Init.DoSomething2: ' 
    + Format('%p, %d', [Pointer(Self), FValue]))); 
end; 

procedure TInits4Ever2Init.DoSomething3; 
begin 
    OutputDebugString(pAnsiChar('-- TInits4Ever2Init.DoSomething3: ' 
    + Format('%p, %d', [Pointer(Self), FValue]))); 
end; 

initialization 
    RegisterTest(TMyTestSetup.Create(TMyTest.Suite)); 
end. 

Come i commenti nel campione risulta, ho usato una variabile privata randomizzato, e qualche uscita di traccia di debug, per confermare che ogni chiamata di prova con la suite di test si trova la stessa copia dell'oggetto di destinazione, ma stiamo ottenendo una copia diversa dell'oggetto di destinazione ogni volta che viene eseguita la suite di test.

4

è possibile derivare una nuova classe Test Suite dalla classe TTestSuite, e sovrascrivere i suoi metodi di impostazione e Teardown, quindi è possibile aggiungere i casi di test per questo particolare suite di test, e registrare la suite.

In questo modo, i metodi di installazione e TearDown della classe della suite di test verranno richiamati una volta, e i metodi SetUp e TearDown di ogni caso di test verranno richiamati per ogni metodo di prova definito in tale caso.

ordine di esecuzione sarà simile a questo:

TestSuite.SetUp; 

-- TestCase1.Setup; 
---- TestCase1.Test1; 
-- TestCase1.TearDown; 
-- TestCase1.Setup; 
---- TestCase1.Test2; 
-- TestCase1.TearDown; 

-- TestCase2.Setup; 
---- TestCase2.Test1; 
-- TestCase2.TearDown; 
-- TestCase2.Setup; 
---- TestCase2.Test2; 
-- TestCase2.TearDown; 

-- TestCaseN.Setup; 
---- TestCaseN.Test1; 
-- TestCaseN.TearDown; 
-- TestCaseN.Setup; 
---- TestCaseN.Test2; 
-- TestCaseN.TearDown; 

TestSuite.TearDown; 
+0

Se ho capito bene, questo è esattamente ciò che faccio derivando TMyTestSetup dalla classe TTestSetup. Non penso che questo sia ciò che TTestSuite dovrebbe fare. –

+0

Penso che tu abbia mischiato TTestSetup e TTestSuite –

+1

@Michael: un DUnit ITestSuite 'is-a' ITest quindi deve fornire i metodi SetUp e TearDown, il che rende sicuro assumere che si supponga di fornire un modo per inizializzare e ripulire l'ambiente del dispositivo di prova. – mjn

1

Utilizzando TTestSetup si potrebbe fare qualcosa di simile:

type 
    TMyTestSetup = class(TTestSetup) 
    private 
    FValue: Integer; 
    protected 
    procedure SetUp; override; 
    procedure TearDown; override; 
    end; 

    TMyTestCase = class(TTestCase) 
    published 
    procedure TestSomething; 
    end; 

var 
    TestSetup: TMyTestSetup; 

procedure TMyTestSetup.SetUp; 
begin 
    inherited; 
    TestSetup := Self; 
    FValue := 42; 
end; 

procedure TMyTestSetup.TearDown; 
begin 
    TestSetup := nil; 
    inherited; 
end; 

procedure TMyTestCase.TestSomething; 
begin 
    CheckEquals(TestSetup.FValue, 42); 
end; 

initialization 
    TestFramework.RegisterTest(TMyTestSetup.Create(
    TTestSuite.Create('My test suite', [TMyTestCase.Suite]) 
)); 

ci si sente un po 'rivoltante si badi bene, ma non il lavoro!

+0

questo non è molto meglio di dichiarare fTakes4Ever2Init globalmente, vero? –

+0

@ Michael No, non proprio! Stavo lentamente arrivando a quella realizzazione da solo. Suppongo che l'unica differenza sia che SetUp/TearDown vengono chiamati ogni volta che esegui dei test. Personalmente probabilmente userò solo un globale. –

2

Non è possibile inizializzare i campi TTestCase per una suite di test intero, e qui è una spiegazione per questo:

unit Tests3; 

interface 

uses 
    TestFramework, TestExtensions, Windows, Forms, Dialogs, Controls, Classes, 
    SysUtils, Variants, Graphics, Messages; 

type 
    TMyTestCase = class(TTestCase) 
    private 
    FValue: Integer; 
    published 
    procedure Test1; 
    procedure Test2; 
    end; 

implementation 

{ TMyTestCase } 

procedure TMyTestCase.Test1; 
begin 
    FValue:= 99; 
    ShowMessage(Format('%p, %d', [Pointer(Self), FValue])); 
end; 

procedure TMyTestCase.Test2; 
begin 
    ShowMessage(Format('%p, %d', [Pointer(Self), FValue])); 
end; 

initialization 
    RegisterTest(TMyTestCase.Suite); 
end. 

Se si esegue il test di unità di cui sopra si vedrà che gli indirizzi del 'sé' mostrati in Test1 e i metodi Test2 sono diversi. Ciò significa che le istanze dell'oggetto TMyTestCase sono diverse per le chiamate Test1 e Test2.

Di conseguenza, qualsiasi campo che si può dichiarare nella classe TMyTestCase è volatile tra le chiamate del metodo di prova.

Per eseguire l'inizializzazione "globale" è necessario dichiarare l'oggetto globalmente, non come campo TMyTestCase.

+0

Ma cosa succede se fai fTakes4Ever2Init un campo della classe TTestSetup? –

+0

@ Michael: Sono d'accordo con la risposta di David - è possibile, ma non puoi accedere ai campi TTestSetup dai metodi TTestCase senza creare riferimenti globali all'istanza TTestSetup o qualcosa del genere. – kludg

1

A seconda della versione Delphi, è sufficiente impostare il campo TMyTest.fTakes4Ever2Init a public class var per inizializzarlo dall'impostazione di prova. (Questo sarebbe più stile OOP rispetto a una variabile globale unitaria.)

3

Avere solo un metodo pubblicato, che a sua volta chiama tutti gli altri metodi di test è il modo pigro ma più rapido di di richiamare la procedura di installazione e di TearDown solo una volta.

Problemi correlati