2011-07-28 12 views
5

mam zasilacz od I1 do ILogger realizowanego tak:Jak napisać JUnit dla Adaptera bez skomplikowanego kodu?

class BAdapter() implements I1 
{ 
    void logA() { // nothing } 
    void logB() { new BLogger().log() } 
    void logC() { // nothing } 
} 

chciałbym napisać testu JUnit, że sprawdzenie funkcjonalności, ale uważam, że to nieco problematyczne, ponieważ nie mogę wprowadzić mój atrapa obiektu zamiast z BLogger lub sprawdź wartość zwracaną. Znalazłem kilka możliwych rozwiązań, ale nie jestem pewien, który jest najlepszy.

Sprawa pierwsza: Dodaj void setLogger(Logger l) do klasy BAdapter.

class BAdapter() implements I1 
{ 
    private Logger logger = new BLogger(); 

    public void logB() { logger.log() } 
    public void setLogger(Logger l) { logger = l } 
    .. //rest of methods 
} 

Minusy: Dlaczego dodać setter, które nigdy nie jest używane w „prawdziwym”, kod zakaz testowania?

Sprawa druga: dodawania chroniony metoda fabryki i sublcass BAdapter w pakiecie testowym.

class BAdapter() implements I1 
{ 
    public void logB() { createLogger().log() } 
    protected Logger createLogger() { retrun new BLogger() } 
    .. //rest of methods 
} 

class BAdapterForTesting extends BAdapter() 
{ 
    protected Logger createLogger() { retrun new MockBLogger() } 
} 

Minusy: Nie jestem pewien, czy to jest czyste i eleganckie rozwiązanie, ale nie widzę tutaj zbyt wielu wad.

Przypadek trzeci: Użyj wzoru fabryka abstrakcyjna.

class BAdapter() implements I1 
{ 
    public void logB() { AbstractFactory.getFactory().getBLogger().log() } 
    .. //rest of methods 
} 

A gdzieś w testach:

AbstractFactory.setFactory(new MockLoggersFactory()) 

Wady: To jest zbyt skomplikowane, prawda?

Przypadek czwarty: Zwraca wartość logiczną, na przykład po przeprowadzeniu rejestrowania. Na przykład.

class BAdapter() implements I1 
{ 
    Boolean logA() { return false; } 
    Boolean logB() { return new BLogger().log() } 
    Boolean logC() { return false; } 
} 

Minusy: To rodzaj wourkaround. Dlaczego warto zwrócić jakąś wartość, gdy nikt jej nie potrzebuje w "prawdziwym", nie testującym kodzie?

Lepsze rozwiązanie? Czy jest coś lepszego?

Odpowiedz

2

Z twojego kodu trudno jest dokładnie określić, co próbowana klasa próbuje osiągnąć, ale Poszedłbym z Case One z zastrzeżeniem, że użyłbym zastrzyku również w "prawdziwym" kodzie.

Jedną z zalet wtrysku jest to, że klasa, w tym przypadku adapter, jest bardziej przydatna do ponownego użycia. Wymuszenie, aby metoda logB zawsze delegowała do instancji BLogger, ustawia to zachowanie w stanie skalnym podczas kompilacji. Jeśli chcę, aby adapter przekazywał inny typ Logger, nie mogę go użyć i dlatego jest nieco mniej przydatny do ponownego użycia.

+0

Znalazłem podobne podejście w JUnit w książce Action. Kod testowy jest klientem twojego kodu, tak jak każdy inny, więc dobrze jest ponownie przeliczyć lub zaktualizować kod zgodnie z wymaganiami testu jednostki. –

0

IMVHO tworzy kod szczególnie do wykorzystania przez testy i nigdzie indziej nie jest dobrym pomysłem, ponieważ może ujawnić funkcjonalność, która nie powinna być używana, ale niektórzy mogą myśleć, że używanie go jest fajne i jest bardzo zaskoczone.

To pozostawia nam przypadek 3, który jest ogólnie bardzo dobrym podejściem, ale jeśli ma być używany tylko podczas testowania, to wciąż jest trochę przesady.

Sugeruję użyciu pewnych dodatkowych ram szyderczy jak PowerMock która może zmienić pola prywatne w zajęciach w wygodny sposób:

class BAdapter() implements I1 
{ 
    private Logger logger = new BLogger(); 

    public void logB() { logger.log() } 
    .. //rest of methods 
} 

a następnie podczas testowania SWAP boiska i niektóre makiety:

Whitebox.setInternalState(loggerInstance, "logger", new MockLogger()); 

więcej informacji: http://code.google.com/p/powermock/wiki/BypassEncapsulation

Szalowanie to wielki atut podczas testów, więc znając ich pomoc bardzo dużo.

Powiązane problemy