2011-02-05 11 views
12

Tworzę testy jednostkowe z DUnit. Mam klasę, której zainicjowanie zajmuje dużo czasu.Jak uzyskać dostęp do pól TTestCase w klasie TTestSetup

Wypisuję klasę TMyTestSetup z TTestSetup i zastępuję jego metodę instalacji. Ta metoda SetUp jest wywoływana tylko raz dla wszystkich testów w mojej TTestCase. Uruchomiłem proces inicjalizacji w procedurze TMyTestSetup.SetUp w celu zwiększenia wydajności.

Mój problem polega na tym, jak uzyskać dostęp do obiektu, który chcę zainicjować, co jest polem mojego TMyTest w klasie TestSetup? Czy jest to jedyny sposób, aby to ogłosić globalnie?

niesprawdzone krótki przykład:

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

Myślę, że źle to rozumiesz. Myślę, że chcesz uzyskać dostęp do instancji 'TMyTestSetup' z' TMyTest', ale mogę się mylić !!! –

+0

Tak czy inaczej nie mam pojęcia, jak to zrealizować. –

+0

Muszę przyznać, że czasami uważam, że DUnit jest trochę nieprzejrzysty! –

Odpowiedz

9

Sztuką jest użycie publicznej zmiennej klasy w klasie TMyTestSetup.

tak (przetestowane i działa, kompletne) przykład:

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. 

Ponieważ komentarze w próbce wskazują, Użyłem randomizowane zmienną prywatną, a niektóre dane wyjściowe śledzenia debugowania, aby potwierdzić, że każde wywołanie testu w pakiecie testowym znajduje się ta sama kopia obiektu docelowego, ale za każdym razem, gdy uruchamiany jest pakiet testów, otrzymujemy inną kopię obiektu docelowego.

4

można czerpać nową klasę Test Suite z klasy TTestSuite i przesłonić jego konfigurację i przerywaniem metod, a następnie można dodać przypadków testowych do tego konkretnego zestawu testowego i zarejestrować apartament.

W ten sposób metody Setup i TearDown klasy zestawu testów będą wywoływane jeden raz, a metody SetUp i TearDown dla każdego przypadku testowego będą wywoływane dla każdej metody testowej zdefiniowanej w tym przypadku testowym.

realizacji zlecenia będzie tak:

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

Jeśli rozumiem to prawo, to jest dokładnie to, co robię, wyprowadzając TMyTestSetup z klasy TTestSetup. Nie sądzę, że TTestSuite ma to robić. –

+0

Myślę, że pomieszałeś TTestSetup i TTestSuite –

+1

@Michael: DUnit ITestSuite 'is-a' ITest, więc musi dostarczyć metody SetUp i TearDown, co pozwala bezpiecznie założyć, że ma on zapewnić sposób inicjalizacji i czyszczenia środowisko testowego urządzenia. – mjn

1

Korzystanie TTestSetup mógłby zrobić coś takiego:

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]) 
)); 

Czuje się nieco odrażający umysł ty, ale spełnia swoje zadanie!

+0

to nie jest dużo lepsze niż ogłaszanie fTakes4Ever2Init na całym świecie, prawda? –

+0

@Michael No not really! Powoli zbliżałem się do tej realizacji. Przypuszczam, że jedyną różnicą jest to, że SetUp/TearDown są wywoływane za każdym razem, gdy przeprowadzasz jakieś testy. Osobiście prawdopodobnie użyłbym globalnego. –

2

Nie można zainicjować pól TTestCase dla całego zestawu testów, a tu jest wyjaśnienie dlaczego:

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. 

Po uruchomieniu powyższego badania urządzenie będzie widać, że odnosi się do „ja” pokazany w Test1 a metody Test2 są różne. Oznacza to, że wystąpienia obiektów TMyTestCase są różne dla wywołań Test1 i Test2.

W związku z tym wszystkie pola, które można zadeklarować w klasie TMyTestCase, są niestabilne między wywołaniami metody testowej.

Aby przeprowadzić inicjalizację "globalną", należy zadeklarować obiekt globalnie, a nie pole TMyTestCase.

+0

Co jednak zrobić, jeśli sprawisz, że fTakes4Ever2Init stanie się polem klasy TTestSetup? –

+0

@Michael: Zgadzam się z odpowiedzią Davida - to jest możliwe, ale nie możesz uzyskać dostępu do pól TTestSetup z metod TTestCase bez tworzenia globalnego odniesienia do instancji TTestSetup lub czegoś podobnego. – kludg

1

W zależności od wersji Delphi można po prostu ustawić pole TMyTest.fTakes4Ever2Init jako public class var, aby zainicjować je z konfiguracji testowej. (To byłoby bardziej styl OOP w porównaniu ze zmienną globalną jednostki.)

3

Posiadanie tylko jednej opublikowanej metody, która z kolei wywoła wszystkie inne metody testowania, jest leniwym, ale szybszym sposobem wywołania procedury Setup i TearDown tylko raz.

Powiązane problemy