Chciałbym przetestować klasę abstrakcyjną. Jasne, mogę ręcznie napisać próbną dziedziczenie po klasie.
Czy mogę to zrobić przy użyciu frameworku (używam Mockito) zamiast ręcznego tworzenia mojej makiety? W jaki sposób?
java
unit-testing
mocking
abstract-class
mockito
ripper234
źródło
źródło
SomeAbstract spy = spy(SomeAbstract.class);
mock(MyAbstractClass.class, withSettings().useConstructor(arg1, arg2).defaultAnswer(CALLS_REAL_METHODS))
Odpowiedzi:
Poniższa sugestia pozwala przetestować klasy abstrakcyjne bez tworzenia „prawdziwej” podklasy - Mock jest podklasą.
użyj
Mockito.mock(My.class, Mockito.CALLS_REAL_METHODS)
, a następnie wyśmiewaj się z wywoływanych metod abstrakcyjnych.Przykład:
Uwaga: Zaletą tego rozwiązania jest to, że nie trzeba implementować metod abstrakcyjnych, o ile nigdy się ich nie wywołuje.
Moim szczerym zdaniem jest to bardziej rozsądne niż używanie szpiega, ponieważ szpieg wymaga instancji, co oznacza, że musisz utworzyć instancję możliwą do natychmiastowego utworzenia podklasy swojej klasy abstrakcyjnej.
źródło
Jeśli potrzebujesz tylko przetestować niektóre konkretne metody bez dotykania któregokolwiek z abstrakcji, możesz użyć
CALLS_REAL_METHODS
(patrz odpowiedź Mortena ), ale jeśli konkretna testowana metoda wywołuje niektóre abstrakty lub niezaimplementowane metody interfejsu, to nie zadziała - Mockito narzeka: „Nie można wywołać prawdziwej metody interfejsu java”.(Tak, to kiepski projekt, ale niektóre frameworki, np. Tapestry 4, w pewien sposób narzucają go tobie.)
Obejściem tego problemu jest odwrócenie tego podejścia - skorzystaj ze zwykłego próbnego zachowania (tj. Wszystko jest wyśmiewane / zakopane) i użyj,
doCallRealMethod()
aby jawnie wywołać konkretną testowaną metodę. Na przykładZaktualizowano, aby dodać:
W przypadku metod innych niż void musisz użyć
thenCallRealMethod()
zamiast tego, np .:W przeciwnym razie Mockito będzie skarżyć się na „Wykryto niedokończone kodowanie”.
źródło
Możesz to osiągnąć za pomocą szpiega (użyj najnowszej wersji Mockito 1.8+).
źródło
Frameworki są zaprojektowane tak, aby ułatwić wyśmiewanie się z zależności testowanej klasy. Gdy używasz frameworku do wyśmiewania klasy, większość frameworków dynamicznie tworzy podklasę i zastępuje implementację metody kodem wykrywającym, kiedy metoda jest wywoływana i zwraca fałszywą wartość.
Podczas testowania klasy abstrakcyjnej chcesz wykonać nieabstrakcyjne metody obiektu podlegającego testowi (SUT), więc ramy próbne nie są tym, czego potrzebujesz.
Częściowe zamieszanie polega na tym, że odpowiedź na pytanie, które podałeś, mówiła o stworzeniu kpiny z twojej abstrakcyjnej klasy. Nie nazwałbym takiej klasy kpiną. Mock to klasa, która jest używana jako zamiennik zależności, jest programowana z oczekiwaniami i może być zapytana, czy te oczekiwania są spełnione.
Zamiast tego sugeruję zdefiniowanie nie-abstrakcyjnej podklasy klasy abstrakcyjnej w teście. Jeśli skutkuje to zbyt dużym kodem, może to oznaczać, że twoja klasa jest trudna do rozszerzenia.
Alternatywnym rozwiązaniem byłoby sprawienie, aby sam przypadek testowy był abstrakcyjny, przy użyciu abstrakcyjnej metody tworzenia SUT (innymi słowy, przypadek testowy użyłby wzorca projektowego Metoda szablonowa).
źródło
Spróbuj użyć niestandardowej odpowiedzi.
Na przykład:
Zwróci próbkę metod abstrakcyjnych i wywoła prawdziwą metodę konkretnych metod.
źródło
To, co naprawdę sprawia, że czuję się źle z wyśmiewaniem klas abstrakcyjnych, to fakt, że ani domyślny konstruktor YourAbstractClass () nie jest wywoływany (brakuje super () w próbnym), ani nie wydaje się, aby Mockito mógł domyślnie inicjować właściwości próbne (np. Właściwości listy z pustą ArrayList lub LinkedList).
Moja klasa abstrakcyjna (w zasadzie generowany jest kod źródłowy klasy) NIE zapewnia wstrzykiwania ustawiacza zależności dla elementów listy, ani konstruktora, w którym inicjuje elementy listy (które próbowałem dodać ręcznie).
Tylko atrybuty klasy używają domyślnej inicjalizacji: private List dep1 = new ArrayList; private List dep2 = new ArrayList
Nie ma więc sposobu na wyśmiewanie się klasy abstrakcyjnej bez użycia rzeczywistej implementacji obiektu (np. Definicja klasy wewnętrznej w klasie testów jednostkowych, przesłanianie metod abstrakcyjnych) i szpiegowanie rzeczywistego obiektu (która dokonuje właściwej inicjalizacji pola).
Szkoda, że tylko PowerMock pomógłby tutaj dalej.
źródło
Zakładając, że twoje klasy testowe znajdują się w tym samym pakiecie (w innym źródłowym katalogu głównym), co klasy testowane, możesz po prostu utworzyć próbkę:
i wywołaj metody, które chcesz przetestować, tak jak każdą inną metodę.
Musisz podać oczekiwania dla każdej metody, która jest wywoływana, z oczekiwaniem na dowolne konkretne metody wywołujące super metodę - nie jestem pewien, jak byś to zrobił za pomocą Mockito, ale wierzę, że jest to możliwe z EasyMock.
Wszystko to polega na stworzeniu konkretnego wystąpienia
YouClass
i oszczędzeniu wysiłku polegającego na zapewnieniu pustych implementacji każdej metody abstrakcyjnej.Nawiasem mówiąc, często uważam za użyteczne zaimplementowanie klasy abstrakcyjnej w moim teście, gdzie służy ona jako przykładowa implementacja, którą testuję za pomocą jej publicznego interfejsu, chociaż zależy to od funkcjonalności zapewnianej przez klasę abstrakcyjną.
źródło
Możesz rozszerzyć klasę abstrakcyjną o anonimową klasę w teście. Na przykład (przy użyciu Junit 4):
źródło
Mockito pozwala wyśmiewać klasy abstrakcyjne za pomocą
@Mock
adnotacji:Wadą jest to, że nie można go użyć, jeśli potrzebujesz parametrów konstruktora.
źródło
Możesz utworzyć anonimową klasę, wstrzyknąć sobie kpiny, a następnie przetestować tę klasę.
Należy pamiętać, że widoczność musi być
protected
własnościąmyDependencyService
klasy abstrakcyjnejClassUnderTest
.źródło
Whitebox.invokeMethod(..)
W tym przypadku przydatne mogą być PowerMock .źródło