Wie kann ich JUnit4 idiomatisch verwenden, um zu testen, ob ein Code eine Ausnahme auslöst?
Während ich so etwas sicher machen kann:
@Test
public void testFooThrowsIndexOutOfBoundsException() {
boolean thrown = false;
try {
foo.doStuff();
} catch (IndexOutOfBoundsException e) {
thrown = true;
}
assertTrue(thrown);
}
Ich erinnere mich, dass es eine Annotation oder eine Assert.xyz oder etwas gibt , das für diese Art von Situationen weit weniger klobig und viel mehr im Geiste von JUnit ist.
org.mockito.Mockito.verify
mit verschiedenen Parametern aufrufen , um sicherzustellen, dass bestimmte Dinge geschehen sind (z. B. dass ein Protokollierungsdienst mit den richtigen Parametern aufgerufen wurde), bevor die Ausnahme ausgelöst wurde.Antworten:
Dies hängt von der JUnit-Version und den von Ihnen verwendeten Assert-Bibliotheken ab.
Die ursprüngliche Antwort für
JUnit <= 4.12
war:Die Antwort https://stackoverflow.com/a/31826781/2986984 bietet jedoch mehr Optionen für JUnit <= 4.12.
Referenz :
quelle
ArrayList
darauf reagiert,get()
ist irrelevant. Wenn ich mich in Zukunft für ein primitives Array entscheiden würde, müsste ich diese Testimplementierung ändern. Die Datenstruktur sollte ausgeblendet sein, damit sich der Test auf das Verhalten der Klasse konzentrieren kann .Bearbeiten: Nachdem JUnit 5 und JUnit 4.13 veröffentlicht wurden, ist die beste Option die Verwendung
Assertions.assertThrows()
(für JUnit 5) undAssert.assertThrows()
(für JUnit 4.13). Siehe meine andere Antwort für Details.Wenn Sie nicht auf JUnit 5 migriert sind, aber JUnit 4.7 verwenden können, können Sie die
ExpectedException
Regel verwenden:Dies ist viel besser als
@Test(expected=IndexOutOfBoundsException.class)
weil der Test fehlschlägt, wennIndexOutOfBoundsException
er zuvor geworfen wirdfoo.doStuff()
Sehen Sie diesen Artikel für weitere Details
quelle
ExpectedException
Klasse Möglichkeiten, die Nachricht der Ausnahme abzugleichen oder sogar einen eigenen Matcher zu schreiben, der von der Klasse der Ausnahme abhängt. Zweitens können Sie Ihre Erwartung unmittelbar vor der Codezeile festlegen, von der Sie erwarten, dass sie die Ausnahme auslöst. Dies bedeutet, dass Ihr Test fehlschlägt, wenn die falsche Codezeile die Ausnahme auslöst. Mit der Lösung von Skaffman gibt es keine Möglichkeit, dies zu tun.Seien Sie vorsichtig mit der erwarteten Ausnahme, da nur behauptet wird, dass die Methode diese Ausnahme ausgelöst hat, nicht eine bestimmte Codezeile im Test.
Ich neige dazu, dies zum Testen der Parametervalidierung zu verwenden, da solche Methoden normalerweise sehr einfach sind, aber komplexere Tests möglicherweise besser bedient werden mit:
Urteil anwenden.
quelle
ExpectedException
ist es normal, die Erwartung unmittelbar vor der Zeile festzulegen, in der die Ausnahme ausgelöst werden soll. Auf diese Weise wird die Regel nicht ausgelöst, wenn eine frühere Zeile die Ausnahme auslöst, und der Test schlägt fehl.Wie bereits erwähnt, gibt es in JUnit viele Möglichkeiten, mit Ausnahmen umzugehen. Bei Java 8 gibt es jedoch noch eines: die Verwendung von Lambda-Ausdrücken. Mit Lambda Expressions können wir eine Syntax wie diese erreichen:
assertThrown akzeptiert eine funktionale Schnittstelle, deren Instanzen mit Lambda-Ausdrücken, Methodenreferenzen oder Konstruktorreferenzen erstellt werden können. assertThrown, das diese Schnittstelle akzeptiert, erwartet und ist bereit, eine Ausnahme zu behandeln.
Dies ist eine relativ einfache und dennoch leistungsstarke Technik.
Schauen Sie sich diesen Blog-Beitrag an, der diese Technik beschreibt: http://blog.codeleak.pl/2014/07/junit-testing-exception-with-java-8-and-lambda-expressions.html
Den Quellcode finden Sie hier: https://github.com/kolorobot/unit-testing-demo/tree/master/src/test/java/com/github/kolorobot/exceptions/java8
Offenlegung: Ich bin der Autor des Blogs und des Projekts.
quelle
new DummyService()::someMethod
einMethodHandle
, aber dieser Ansatz funktioniert genauso gut mit Lambda-Ausdrücken.In junit gibt es vier Möglichkeiten, eine Ausnahme zu testen.
junit5.x
Für junit5.x können Sie
assertThrows
Folgendes verwendenjunit4.x
Verwenden Sie für junit4.x das optionale Attribut 'erwartet' der Testannonation
Verwenden Sie für junit4.x die ExpectedException-Regel
Sie können auch den klassischen Try / Catch-Weg verwenden, der unter dem Junit 3-Framework weit verbreitet ist
damit
Weitere Informationen finden Sie in diesem Dokument und im junit5-Benutzerhandbuch .
quelle
Trowable
an die Methode übergeben könnenExpectedException.expect
. Bitte sehen Sie die Unterschrift . @miusertl; dr
Post-JDK8: Verwenden Sie AssertJ oder benutzerdefinierte Lambdas, um außergewöhnliches Verhalten zu behaupten .
Pre-JDK8: Ich werde die alten gut empfehlen
try
-catch
Block. ( Vergessen Sie nicht,fail()
vor demcatch
Block eine Zusicherung hinzuzufügen. )Unabhängig von Junit 4 oder JUnit 5.
die lange Geschichte
Es ist möglich , sich selbst zu schreiben , ein do it yourself
try
-catch
Block oder verwenden Sie die JUnit - Tools (@Test(expected = ...)
oder die@Rule ExpectedException
Funktion JUnit - Regel).Diese Methoden sind jedoch nicht so elegant und lassen sich in Bezug auf die Lesbarkeit nicht gut mit anderen Tools kombinieren. Darüber hinaus weist JUnit Tooling einige Fallstricke auf.
Der
try
-catch
Block Sie müssen den Block um das getestete Verhalten schreiben und die Behauptung in den catch-Block schreiben. Das mag in Ordnung sein, aber viele finden, dass dieser Stil den Lesefluss eines Tests unterbricht. Außerdem müssen SieAssert.fail
am Ende destry
Blocks ein schreiben . Andernfalls kann der Test eine Seite der Behauptungen verfehlen. PMD , Findbugs oder Sonar werden solche Probleme erkennen.Die
@Test(expected = ...)
Funktion ist interessant, da Sie weniger Code schreiben können und das Schreiben dieses Tests angeblich weniger anfällig für Codierungsfehler ist. Aber dieser Ansatz ist in einigen Bereichen fehlt.Auch wenn die Erwartung in die Methode gestellt wird, kann der falsche Teil des Testcodes abhängig davon, wie der getestete Code geschrieben wird, die Ausnahme auslösen , was zu einem falsch positiven Test führt, und ich bin nicht sicher, ob PMD , Findbugs oder Sonar gibt Hinweise zu diesem Code.
Die
ExpectedException
Regel ist auch ein Versuch, die vorherigen Einschränkungen zu beheben. Die Verwendung ist jedoch etwas umständlich, da ein Erwartungsstil verwendet wird. EasyMock- Benutzer kennen diesen Stil sehr gut. Für einige mag es praktisch sein, aber wenn Sie die Prinzipien von Behavior Driven Development (BDD) oder Arrange Act Assert (AAA) befolgen,ExpectedException
passt die Regel nicht in diesen Schreibstil. Abgesehen davon kann es unter dem gleichen Problem wie der@Test
Weg leiden , je nachdem, wo Sie die Erwartung platzieren.Selbst wenn die erwartete Ausnahme vor der Testanweisung steht, wird Ihr Lesefluss unterbrochen, wenn die Tests BDD oder AAA folgen.
Siehe auch diese Kommentarausgabe zu JUnit des Autors von
ExpectedException
. JUnit 4.13-beta-2 lehnt diesen Mechanismus sogar ab:Diese oben genannten Optionen haben also alle Vorbehalte und sind eindeutig nicht immun gegen Codiererfehler.
Es gibt ein Projekt, auf das ich aufmerksam geworden bin, nachdem ich diese Antwort erstellt habe, das vielversprechend aussieht. Es ist eine Ausnahme .
Wie in der Beschreibung des Projekts angegeben, konnte ein Codierer eine fließende Codezeile schreiben, die die Ausnahme abfängt, und diese Ausnahme für die letztere Behauptung anbieten. Und Sie können jede Assertionsbibliothek wie Hamcrest oder AssertJ verwenden .
Ein schnelles Beispiel von der Homepage:
Wie Sie sehen können, ist der Code wirklich einfach. Sie fangen die Ausnahme in einer bestimmten Zeile ab. Die
then
API ist ein Alias, der AssertJ-APIs verwendet (ähnlich wie bei der VerwendungassertThat(ex).hasNoCause()...
). Irgendwann stützte sich das Projekt auf FEST-Assert, den Vorfahren von AssertJ . EDIT: Es scheint, dass das Projekt eine Java 8 Lambdas-Unterstützung erstellt.Derzeit weist diese Bibliothek zwei Mängel auf:
Zum Zeitpunkt dieses Schreibens ist es bemerkenswert zu sagen, dass diese Bibliothek auf Mockito 1.x basiert, da sie ein Modell des getesteten Objekts hinter den Kulissen erstellt. Da Mockito immer noch nicht aktualisiert wird, kann diese Bibliothek nicht mit endgültigen Klassen oder endgültigen Methoden arbeiten . Und selbst wenn es in der aktuellen Version auf Mockito 2 basiert, müsste ein globaler Mock Maker (
inline-mock-maker
) deklariert werden , was möglicherweise nicht Ihren Wünschen entspricht, da dieser Mock Maker andere Nachteile hat als der reguläre Mock Maker.Es erfordert noch eine weitere Testabhängigkeit.
Diese Probleme treten nicht auf, sobald die Bibliothek Lambdas unterstützt. Die Funktionalität wird jedoch vom AssertJ-Toolset dupliziert.
Unter Berücksichtigung aller Aspekte, wenn Sie das Catch-Exception-Tool nicht verwenden möchten, empfehle ich den alten guten Weg des
try
-catch
Blocks, zumindest bis zum JDK7. Und für JDK 8-Benutzer bevorzugen Sie möglicherweise die Verwendung von AssertJ, da es möglicherweise mehr als nur das Festlegen von Ausnahmen bietet.Mit dem JDK8 betreten Lambdas die Testszene und haben sich als interessante Möglichkeit erwiesen, außergewöhnliches Verhalten zu behaupten. AssertJ wurde aktualisiert, um eine flüssige API bereitzustellen, mit der außergewöhnliches Verhalten bestätigt werden kann.
Und ein Beispieltest mit AssertJ :
Mit einer nahezu vollständigen Neufassung von JUnit 5 wurden die Behauptungen ein wenig verbessert . Sie können sich als sofort einsatzbereit erweisen, um eine ordnungsgemäße Ausnahme zu behaupten. Aber wirklich, die Assertion-API ist immer noch ein bisschen schlecht, da draußen ist nichts
assertThrows
.Wie Sie bemerkt haben,
assertEquals
kehrt es immer noch zurückvoid
und erlaubt daher keine Verkettung von Behauptungen wie AssertJ.Auch wenn Sie sich an Namenskonflikte mit
Matcher
oder erinnernAssert
, sollten Sie darauf vorbereitet sein, denselben Konflikt mit zu treffenAssertions
.Ich möchte zum Schluss kommen, dass heute ( 03.03.2017 ) AssertJs Benutzerfreundlichkeit, erkennbare API, das schnelle Entwicklungstempo und als De-facto- Testabhängigkeit die beste Lösung mit JDK8 sind, unabhängig vom Testframework (JUnit) oder nicht), frühere JDKs sollten sich stattdessen auf
try
-catch
Blöcke verlassen, selbst wenn sie sich klobig fühlen.Diese Antwort wurde von einer anderen Frage kopiert , die nicht die gleiche Sichtbarkeit hat. Ich bin der gleiche Autor.
quelle
Nachdem JUnit 5 und JUnit 4.13 veröffentlicht wurden, ist die beste Option die Verwendung
Assertions.assertThrows()
(für JUnit 5) undAssert.assertThrows()
(für JUnit 4.13). Siehe das Junit 5-Benutzerhandbuch .Hier ist ein Beispiel, das überprüft, ob eine Ausnahme ausgelöst wird, und mithilfe von Truth Aussagen über die Ausnahmemeldung macht:
Die Vorteile gegenüber den Ansätzen in den anderen Antworten sind:
throws
Klausel auflistenEine ähnliche Methode wird
org.junit Assert
in JUnit 4.13 hinzugefügt .quelle
Wie wäre es damit: Fangen Sie eine sehr allgemeine Ausnahme ab, stellen Sie sicher, dass sie aus dem catch-Block herauskommt, und behaupten Sie dann, dass die Klasse der Ausnahme so ist, wie Sie es erwarten. Diese Behauptung schlägt fehl, wenn a) die Ausnahme vom falschen Typ ist (z. B. wenn Sie stattdessen einen Nullzeiger erhalten haben) und b) die Ausnahme nie ausgelöst wurde.
quelle
assertEquals(ExpectedException.class, e.getClass())
zeigt Ihnen die erwarteten und tatsächlichen Werte an, wenn der Test fehlschlägt.BDD- Lösung: JUnit 4 + Catch Exception + AssertJ
Abhängigkeiten
quelle
Verwenden einer AssertJ- Assertion, die neben JUnit verwendet werden kann:
Es ist besser als
@Test(expected=IndexOutOfBoundsException.class)
weil es garantiert, dass die erwartete Zeile im Test die Ausnahme ausgelöst hat und Sie mehr Details über die Ausnahme, wie z. B. eine Nachricht, einfacher überprüfen können:Maven / Gradle Anleitung hier.
quelle
assertThat
, sondern immer die AssertJ-Bibliothek zu verwenden. Außerdem gibt die JUnit-Methode nur einen "regulären" Typ zurück, während die AssertJ-Methode eineAbstractAssert
Unterklasse zurückgibt ... die das Aneinanderreihen von Methoden wie oben ermöglicht (oder was auch immer die technischen Begriffe dafür sind ...).Um das gleiche Problem zu lösen, habe ich ein kleines Projekt eingerichtet: http://code.google.com/p/catch-exception/
Mit diesem kleinen Helfer würdest du schreiben
Dies ist weniger ausführlich als die ExpectedException-Regel von JUnit 4.7. Im Vergleich zu der von skaffman bereitgestellten Lösung können Sie angeben, in welcher Codezeile Sie die Ausnahme erwarten. Ich hoffe das hilft.
quelle
foo
istfinal
es fehl , weil Sie nicht Proxyfoo
?Update: JUnit5 hat eine Verbesserung für das Testen von Ausnahmen :
assertThrows
.Das folgende Beispiel stammt aus: Junit 5 Benutzerhandbuch
Ursprüngliche Antwort mit JUnit 4.
Es gibt verschiedene Möglichkeiten, um zu testen, ob eine Ausnahme ausgelöst wird. Ich habe auch die folgenden Optionen in meinem Beitrag besprochen. Wie schreibe ich großartige Unit-Tests mit JUnit?
Stellen Sie den
expected
Parameter ein@Test(expected = FileNotFoundException.class)
.Verwenden von
try
catch
Testen mit
ExpectedException
Regel.Weitere Informationen zu Ausnahmetests finden Sie im JUnit4-Wiki für Ausnahmetests und in bad.robot - Expecting Exceptions JUnit Rule .
quelle
Sie können dies auch tun:
quelle
Assert.fail()
nicht zu verwendenassert
, nur für den Fall, dass Ihre Tests in einer Umgebung ausgeführt werden, in der Zusicherungen nicht aktiviert sind.IMHO ist der beste Weg, um in JUnit nach Ausnahmen zu suchen, das Muster try / catch / fail / assert:
Das
assertTrue
könnte für einige Leute ein bisschen stark sein, alsoassertThat(e.getMessage(), containsString("the message");
könnte es vorzuziehen sein.quelle
JUnit 5-Lösung
Weitere Informationen zu JUnit 5 finden Sie unter http://junit.org/junit5/docs/current/user-guide/#writing-tests-assertions
quelle
expectThrows()
ist ein Teil von TestNG, kein JUnitDie flexibelste und eleganteste Antwort für Junit 4, die ich im Mkyong-Blog gefunden habe . Es hat die Flexibilität
try/catch
, die@Rule
Annotation zu verwenden. Ich mag diesen Ansatz, weil Sie bestimmte Attribute einer benutzerdefinierten Ausnahme lesen können.quelle
Ich habe viele der Methoden hier ausprobiert, aber sie waren entweder kompliziert oder entsprachen nicht ganz meinen Anforderungen. Tatsächlich kann man eine Hilfsmethode ganz einfach schreiben:
Verwenden Sie es so:
Keine Abhängigkeiten: Keine Notwendigkeit für Mockito, keine Notwendigkeit für Powermock; und funktioniert gut mit Abschlussklassen.
quelle
Java 8-Lösung
Wenn Sie eine Lösung wünschen, die:
Hier ist eine Utility-Funktion, die ich geschrieben habe:
( aus meinem Blog )
Verwenden Sie es wie folgt:
quelle
JUnit hat hierfür eine integrierte Unterstützung mit einem "erwarteten" Attribut
quelle
In meinem Fall erhalte ich immer RuntimeException von db, aber die Nachrichten unterscheiden sich. Und Ausnahmen müssen jeweils behandelt werden. So habe ich es getestet:
quelle
} catch (
sollten Sie einfügenfail("no exception thrown");
Erstellen Sie einfach einen Matcher, der wie folgt ein- und ausgeschaltet werden kann:
Um es zu benutzen:
dann füge hinzu
public ExpectedException exception = ExpectedException.none();
:quelle
In JUnit 4 oder höher können Sie die Ausnahmen wie folgt testen
Dies bietet viele Funktionen, mit denen unsere JUnit-Tests verbessert werden können.
Wenn Sie das folgende Beispiel sehen, teste ich 3 Dinge für die Ausnahme.
quelle
Wir können einen Assertion Fail nach der Methode verwenden, die eine Ausnahme zurückgeben muss:
quelle
catch
würde die Stapelverfolgung verschlucken, wenn eine andere Ausnahme ausgelöst wird und nützliche Informationen verloren gehenStellen Sie zusätzlich zu den Aussagen von NamShubWriter Folgendes sicher:
Mach das nicht :
Schließlich zeigt dieser Blog-Beitrag deutlich, wie eine bestimmte Ausnahme ausgelöst werden kann.
quelle
Ich empfehle die Bibliothek
assertj-core
, um Ausnahmen im Junit-Test zu behandelnIn Java 8 wie folgt:
quelle
Die Junit4-Lösung mit Java8 besteht darin, diese Funktion zu verwenden:
Verwendung ist dann:
Beachten Sie, dass die einzige Einschränkung darin besteht, eine
final
Objektreferenz im Lambda-Ausdruck zu verwenden. Diese Lösung ermöglicht es, Testzusicherungen fortzusetzen, anstatt zu erwarten, dass sie auf Methodenebene mit der@Test(expected = IndexOutOfBoundsException.class)
Lösung verwendet werden können.quelle
Nehmen wir zum Beispiel, Sie möchten Junit für das unten genannte Codefragment schreiben
Der obige Code dient zum Testen auf eine unbekannte Ausnahme, die auftreten kann, und der folgende dient zum Aktivieren einer Ausnahme mit einer benutzerdefinierten Nachricht.
quelle
Hier ist eine andere Möglichkeit, um zu überprüfen, ob die Methode die richtige Ausnahme ausgelöst hat oder nicht.
quelle
Das JUnit-Framework verfügt über folgende
assertThrows()
Methode:org.junit.jupiter.api.Assertions
Klasse;org.junit.Assert
Klasse;org.junit.jupiter:junit-jupiter-api
Ihr Projekt hinzu, und Sie erhalten eine perfekt funktionierende Version von JUnit 5.quelle
Mit Java 8 können Sie eine Methode erstellen, die einen Code zum Überprüfen und die erwartete Ausnahme als Parameter verwendet:
und dann in Ihrem Test:
Leistungen:
quelle