Jak mogę używać JUnit4 idiomatycznie do testowania, czy jakiś kod zgłasza wyjątek?
Chociaż z pewnością mogę zrobić coś takiego:
@Test
public void testFooThrowsIndexOutOfBoundsException() {
boolean thrown = false;
try {
foo.doStuff();
} catch (IndexOutOfBoundsException e) {
thrown = true;
}
assertTrue(thrown);
}
Pamiętam, że istnieje adnotacja lub plik Assert.xyz lub coś , co jest znacznie mniej niezgrabne i znacznie bardziej zgodne z duchem JUnit dla tego rodzaju sytuacji.
org.mockito.Mockito.verify
z różnymi parametrami, aby upewnić się, że pewne rzeczy się zdarzyły (takie, że usługa rejestratora została wywołana z poprawnymi parametrami) przed zgłoszeniem wyjątku.Odpowiedzi:
Zależy to od wersji JUnit i używanych bibliotek asercji.
Oryginalna odpowiedź
JUnit <= 4.12
brzmiała:Chociaż odpowiedź https://stackoverflow.com/a/31826781/2986984 ma więcej opcji dla JUnit <= 4.12.
Odniesienie :
źródło
ArrayList
odpowiada,get()
jest nieistotny. Gdybym w przyszłości zdecydował się przejść na prymitywną tablicę, musiałbym zmienić tę implementację testową. Struktura danych powinna być ukryta, aby test mógł skupić się na zachowaniu klasy .Edycja: Teraz, gdy JUnit 5 i JUnit 4.13 zostały wydane, najlepszą opcją byłoby użycie
Assertions.assertThrows()
(dla JUnit 5) iAssert.assertThrows()
(dla JUnit 4.13). Zobacz moją drugą odpowiedź aby poznać szczegóły.Jeśli nie przeprowadziłeś migracji do JUnit 5, ale możesz korzystać z JUnit 4.7, możesz użyć
ExpectedException
reguły:Jest to o wiele lepsze niż
@Test(expected=IndexOutOfBoundsException.class)
ponieważ test zakończy się niepowodzeniem, jeśliIndexOutOfBoundsException
zostanie wcześniej rzuconyfoo.doStuff()
Zobacz ten artykuł szczegóły
źródło
ExpectedException
klasa ma sposoby dopasowania komunikatu wyjątku, a nawet napisania własnego elementu dopasowującego, który zależy od klasy wyjątku. Po drugie, możesz ustawić swoje oczekiwania bezpośrednio przed wierszem kodu, w którym chcesz zgłosić wyjątek - co oznacza, że test zakończy się niepowodzeniem, jeśli niewłaściwy wiersz kodu zgłasza wyjątek; podczas gdy rozwiązanie skaffmana nie jest możliwe.Zachowaj ostrożność, używając oczekiwanego wyjątku, ponieważ zapewnia on jedynie, że metoda zgłosiła ten wyjątek, a nie określoną linię kodu w teście.
Zwykle używam tego do testowania sprawdzania poprawności parametrów, ponieważ takie metody są zwykle bardzo proste, ale bardziej złożone testy mogą być lepiej obsługiwane z:
Zastosuj osąd.
źródło
ExpectedException
normalną czynnością jest ustawienie oczekiwania bezpośrednio przed linią, w której ma zostać zgłoszony wyjątek. W ten sposób, jeśli wcześniejsza linia zgłosi wyjątek, nie uruchomi reguły i test zakończy się niepowodzeniem.Jak już wcześniej wspomniano, istnieje wiele sposobów radzenia sobie z wyjątkami w JUnit. Ale w Javie 8 jest jeszcze jedna: użycie wyrażeń Lambda. Dzięki wyrażeniom Lambda możemy uzyskać następującą składnię:
assertThrown akceptuje interfejs funkcjonalny, którego instancje można tworzyć za pomocą wyrażeń lambda, referencji metod lub referencji konstruktora. assertThrown akceptujący ten interfejs będzie oczekiwać i będzie gotowy do obsługi wyjątku.
Jest to stosunkowo prosta, ale potężna technika.
Obejrzyj ten post na blogu opisujący tę technikę: http://blog.codeleak.pl/2014/07/junit-testing-exception-with-java-8-and-lambda-expressions.html
Kod źródłowy można znaleźć tutaj: https://github.com/kolorobot/unit-testing-demo/tree/master/src/test/java/com/github/kolorobot/exceptions/java8
Ujawnienie: Jestem autorem bloga i projektu.
źródło
new DummyService()::someMethod
jestMethodHandle
, ale to podejście działa równie dobrze z wyrażeniami lambda.w junit istnieją cztery sposoby testowania wyjątku.
junit5.x
dla junit5.x, możesz użyć
assertThrows
w następujący sposóbjunit4.x
w przypadku junit4.x użyj opcjonalnego „oczekiwanego” atrybutu anulowania testu
w przypadku junit4.x użyj reguły ExpectedException
możesz także użyć klasycznego sposobu try / catch, szeroko stosowanego w ramach junit 3
więc
Aby uzyskać więcej informacji, możesz przeczytać ten dokument i podręcznik użytkownika junit5 w celu uzyskania szczegółowych informacji.
źródło
Trowable
metodyExpectedException.expect
. zobacz jego podpis . @miusertl; dr
po JDK8: Użyj AssertJ lub niestandardowych lambd, aby potwierdzić wyjątkowe zachowanie.
pre-JDK8: Ja polecam ten stary dobry
try
-catch
bloku. ( Nie zapomnij dodaćfail()
asercji przedcatch
blokiem )Niezależnie od Junit 4 lub JUnit 5.
długa historia
Możliwe jest napisanie zrób to sam
try
-catch
zablokuj lub użyj narzędzi JUnit (@Test(expected = ...)
lub@Rule ExpectedException
funkcji reguły JUnit).Ale te sposoby nie są tak eleganckie i nie łączą dobrze czytelności z innymi narzędziami. Co więcej, oprzyrządowanie JUnit ma pewne pułapki.
try
-catch
blok trzeba napisać blok wokół badanego zachowania i napisać twierdzenie w bloku catch, że może być w porządku, ale wielu uważa, że ten styl przerywa przepływ odczytu testu. Musisz także napisaćAssert.fail
na końcutry
bloku. W przeciwnym razie test może pominąć jedną ze stron twierdzeń; PMD , findbugs lub Sonar wykryją takie problemy.Ta
@Test(expected = ...)
funkcja jest interesująca, ponieważ możesz napisać mniej kodu, a następnie napisanie tego testu jest prawdopodobnie mniej podatne na błędy kodowania. Jednak w niektórych obszarach brakuje tego podejścia.Ponieważ w metodzie spodziewane jest oczekiwanie, w zależności od tego, jak napisany jest testowany kod, niewłaściwa część kodu testowego może zgłosić wyjątek, co prowadzi do fałszywie dodatniego testu i nie jestem pewien, czy PMD , findbugs lub Sonar poda wskazówki dotyczące takiego kodu.
ExpectedException
Zasada jest również próbą naprawić wcześniejsze zastrzeżenia, ale to jest trochę niewygodne w użyciu, ponieważ wykorzystuje styl oczekiwania, EasyMock użytkownicy wiedzą bardzo dobrze ten styl. Dla niektórych może być to wygodne, ale jeśli przestrzegasz zasad Behaviour Driven Development (BDD) lub Arrange Act Assert (AAA),ExpectedException
reguła nie będzie pasować do tego stylu pisania. Oprócz tego może to dotyczyć tego samego problemu co@Test
droga, w zależności od tego, gdzie stawiasz oczekiwania.Nawet oczekiwany wyjątek jest umieszczony przed instrukcją testu, przerywa przepływ odczytu, jeśli testy są zgodne z BDD lub AAA.
Zobacz także ten komentarz do JUnit autora
ExpectedException
. JUnit 4.13-beta-2 nawet przestaje działać w tym mechanizmie:Tak więc powyższe opcje mają całą masę zastrzeżeń i wyraźnie nie są odporne na błędy kodera.
Jest taki projekt, o którym zdałem sobie sprawę po utworzeniu tej odpowiedzi, która wygląda obiecująco, jest to wyjątek dla wszystkich .
Jak mówi opis projektu, pozwala programistom pisać płynnym wierszem kodu przechwytującego wyjątek i oferuje ten wyjątek dla tego ostatniego stwierdzenia. I możesz użyć dowolnej biblioteki asercji, takiej jak Hamcrest lub AssertJ .
Szybki przykład zaczerpnięty ze strony głównej:
Jak widać, kod jest naprawdę prosty,
then
wychwytujesz wyjątek w określonej linii, API jest aliasem, który będzie używał API AssertJ (podobnie jak przy użyciuassertThat(ex).hasNoCause()...
). W pewnym momencie projekt polegał na FEST-Assert, przodku AssertJ . EDYCJA: Wygląda na to, że projekt przygotowuje obsługę Java 8 Lambdas.Obecnie ta biblioteka ma dwie wady:
W chwili pisania tego tekstu warto wspomnieć, że ta biblioteka oparta jest na Mockito 1.x, ponieważ tworzy próbkę testowanego obiektu za sceną. Ponieważ Mockito wciąż nie jest aktualizowany, ta biblioteka nie może współpracować z ostatecznymi klasami lub ostatecznymi metodami . I nawet jeśli był oparty na Mockito 2 w bieżącej wersji, wymagałoby to zadeklarowania globalnego makiety (
inline-mock-maker
), co może nie być tym, czego chcesz, ponieważ ten makiet ma inne wady niż zwykły makieta.Wymaga to kolejnej zależności testowej.
Te problemy nie będą obowiązywać, gdy biblioteka będzie obsługiwać lambdas. Jednak funkcjonalność zostanie zduplikowana przez zestaw narzędzi AssertJ.
Biorąc to wszystko pod uwagę, jeśli nie chcesz używać narzędzia do wychwytywania wyjątków, polecę stary dobry sposób bloku
try
-catch
przynajmniej do JDK7. A dla użytkowników JDK 8 wolisz korzystać z AssertJ, ponieważ oferuje więcej niż tylko wyjątki.Dzięki JDK8 lambdy wchodzą na scenę testową i okazały się interesującym sposobem na zachowanie wyjątkowego zachowania. AssertJ został zaktualizowany, aby zapewnić przyjemny, płynny interfejs API do zapewnienia wyjątkowego zachowania.
I przykładowy test z AssertJ :
Dzięki prawie całkowitemu przepisaniu JUnit 5, asercje zostały nieco ulepszone , mogą okazać się interesujące jako nieszablonowy sposób na zapewnienie właściwego wyjątku. Ale tak naprawdę interfejs API asercji jest nadal trochę kiepski, na zewnątrz nie ma nic
assertThrows
.Jak zauważyłeś,
assertEquals
wciąż powracavoid
i jako takie nie pozwala na łączenie łańcuchów takich jak AssertJ.Również, jeśli pamiętasz starcie nazwy z
Matcher
lubAssert
, bądź przygotowany na spotkanie tego samego starcia zAssertions
.Chciałbym podsumować, że dziś (2017-03-03) łatwość użycia AssertJ , wykrywalny API, szybkie tempo rozwoju i de facto zależność od testów jest najlepszym rozwiązaniem z JDK8 niezależnie od frameworka testowego (JUnit lub nie), wcześniejsze pakiety JDK powinny zamiast tego polegać na
try
-catch
bloki nawet jeśli czują się niezgrabne.Ta odpowiedź została skopiowana z innego pytania , które nie mają tej samej widoczności, jestem tym samym autorem.
źródło
Teraz, gdy JUnit 5 i JUnit 4.13 zostały wydane, najlepszą opcją byłoby użycie
Assertions.assertThrows()
(dla JUnit 5) iAssert.assertThrows()
(dla JUnit 4.13). Zobacz Podręcznik użytkownika Junit 5 .Oto przykład, który weryfikuje zgłoszony wyjątek i używa Prawdy, aby twierdzić o komunikacie wyjątku:
Zalety w stosunku do podejść przedstawionych w innych odpowiedziach to:
throws
klauzuliPodobna metoda zostanie dodana
org.junit Assert
w JUnit 4.13.źródło
Co powiesz na to: Złap bardzo ogólny wyjątek, upewnij się, że wykracza on z bloku catch, a następnie upewnij się, że klasa wyjątku jest taka, jakiej się spodziewasz. To twierdzenie zakończy się niepowodzeniem, jeśli a) wyjątek jest niewłaściwego typu (np. Jeśli zamiast tego otrzymasz wskaźnik zerowy) ib) wyjątek nigdy nie został zgłoszony.
źródło
assertEquals(ExpectedException.class, e.getClass())
pokaże oczekiwane i rzeczywiste wartości, gdy test się nie powiedzie.Rozwiązanie w stylu BDD : JUnit 4 + wyjątek Catch + AssertJ
Zależności
źródło
Korzystanie z asercji AssertJ , której można używać razem z JUnit:
Jest to lepsze niż
@Test(expected=IndexOutOfBoundsException.class)
ponieważ gwarantuje, że oczekiwana linia w teście rzuciła wyjątek i pozwala sprawdzić więcej szczegółów na temat wyjątku, takich jak wiadomość, łatwiej:Instrukcje Maven / Gradle tutaj.
źródło
assertThat
, zawsze tej z AssertJ. Również metoda JUnit zwraca tylko „zwykły” typ, podczas gdy metoda AssertJ zwracaAbstractAssert
podklasę… pozwalającą na ciąg znaków metod jak wyżej (lub cokolwiek technicznego terminu na to…).Aby rozwiązać ten sam problem, stworzyłem mały projekt: http://code.google.com/p/catch-exception/
Za pomocą tego małego pomocnika piszesz
Jest to mniej szczegółowe niż reguła ExpectedException JUnit 4.7. W porównaniu do rozwiązania dostarczonego przez skaffman możesz określić, w której linii kodu oczekujesz wyjątku. Mam nadzieję, że to pomoże.
źródło
foo
to sięfinal
nie powiedzie, ponieważ nie możesz proxyfoo
?Aktualizacja: JUnit5 ma ulepszenie do testowania wyjątków:
assertThrows
.następujący przykład pochodzi z: Podręcznik użytkownika Junit 5
Oryginalna odpowiedź przy użyciu JUnit 4.
Istnieje kilka sposobów sprawdzenia, czy zgłoszony jest wyjątek. Omówiłem również poniższe opcje w moim poście Jak pisać świetne testy jednostkowe z JUnit
Ustaw
expected
parametr@Test(expected = FileNotFoundException.class)
.Za pomocą
try
catch
Testowanie z
ExpectedException
regułą.Możesz przeczytać więcej o testowaniu wyjątków na wiki JUnit4 dla testów wyjątków i bad.robot - Oczekiwanie na regułę JUnit wyjątków .
źródło
Możesz także to zrobić:
źródło
Assert.fail()
, nieassert
na wypadek, gdyby testy były wykonywane w środowisku, w którym asercje nie są włączone.IMHO, najlepszym sposobem sprawdzenia wyjątków w JUnit jest wzorzec try / catch / fail / assert:
assertTrue
Może być nieco silny dla niektórych osób, więcassertThat(e.getMessage(), containsString("the message");
może być korzystne.źródło
Rozwiązanie JUnit 5
Więcej informacji o JUnit 5 na http://junit.org/junit5/docs/current/user-guide/#writing-tests-assertions
źródło
expectThrows()
jest częścią TestNG, a nie JUnitNajbardziej elastyczną i elegancką odpowiedź na Junit 4 znalazłem na blogu Mkyong . Ma elastyczność
try/catch
korzystania z@Rule
adnotacji. Podoba mi się to podejście, ponieważ możesz odczytać określone atrybuty niestandardowego wyjątku.źródło
Wypróbowałem wiele metod tutaj, ale były one albo skomplikowane, albo nie całkiem spełniały moje wymagania. W rzeczywistości można napisać metodę pomocniczą po prostu:
Użyj tego w ten sposób:
Zero zależności: brak potrzeby mockito, brak potrzeby powermock; i działa dobrze z końcowymi klasami.
źródło
Rozwiązanie Java 8
Jeśli potrzebujesz rozwiązania, które:
Oto funkcja narzędzia, którą napisałem:
( wzięte z mojego bloga )
Użyj go w następujący sposób:
źródło
JUnit ma wbudowaną obsługę tego, z atrybutem „oczekiwany”
źródło
W moim przypadku zawsze otrzymuję RuntimeException z db, ale komunikaty różnią się. I wyjątek należy odpowiednio obsłużyć. Oto jak to przetestowałem:
źródło
} catch (
należy wstawićfail("no exception thrown");
Po prostu utwórz Matchera, który można wyłączyć i włączyć w następujący sposób:
Aby go użyć:
dodaj
public ExpectedException exception = ExpectedException.none();
, a następnie:źródło
W JUnit 4 lub nowszym możesz przetestować wyjątki w następujący sposób
zapewnia to wiele funkcji, które można wykorzystać do ulepszenia naszych testów JUnit.
Jeśli widzisz poniższy przykład, testuję 3 rzeczy na wyjątku.
źródło
Możemy użyć niepowodzenia asercji po metodzie, która musi zwrócić wyjątek:
źródło
catch
Oprócz tego, co powiedział NamShubWriter , upewnij się, że:
Czy nie to zrobić:
Wreszcie, ten post na blogu jasno ilustruje, jak stwierdzić, że został zgłoszony pewien wyjątek.
źródło
Polecam bibliotekę
assertj-core
obsługi wyjątku w teście junitW java 8:
źródło
Rozwiązanie Junit4 z Java8 ma korzystać z tej funkcji:
Użycie jest wtedy:
Zauważ, że jedynym ograniczeniem jest użycie
final
odwołania do obiektu w wyrażeniu lambda. To rozwiązanie pozwala kontynuować testowanie asercji zamiast oczekiwać możliwości zastosowania na poziomie metody za pomocą@Test(expected = IndexOutOfBoundsException.class)
rozwiązania.źródło
Weźmy na przykład, że chcesz napisać Junit dla niżej wymienionego fragmentu kodu
Powyższy kod służy do testowania nieznanych wyjątków, które mogą wystąpić, a poniżej do potwierdzenia wyjątku za pomocą niestandardowego komunikatu.
źródło
Oto inny sposób sprawdzenia, czy metoda zgłosiła poprawny wyjątek, czy nie.
źródło
Framework JUnit ma
assertThrows()
metodę:org.junit.jupiter.api.Assertions
klasie;org.junit.Assert
klasie;org.junit.jupiter:junit-jupiter-api
do swojego projektu, a otrzymasz doskonale działającą wersję z JUnit 5.źródło
W Javie 8 możesz utworzyć metodę, która pobierze kod do sprawdzenia i oczekiwany wyjątek jako parametry:
a następnie w teście:
Korzyści:
źródło