Ich weiß, dass ein Weg, dies zu tun, wäre:
@Test
public void foo(){
try{
//execute code that you expect not to throw Exceptions.
}
catch(Exception e){
fail("Should not have thrown any exception");
}
}
Gibt es einen saubereren Weg, dies zu tun? (Vermutlich mit Junits @Rule
?)
java
unit-testing
junit
exception-handling
junit4
Ankit Dhingra
quelle
quelle
Antworten:
Sie nähern sich dem falsch. Testen Sie einfach Ihre Funktionalität: Wenn eine Ausnahme ausgelöst wird, schlägt der Test automatisch fehl. Wenn keine Ausnahme ausgelöst wird, werden Ihre Tests alle grün angezeigt.
Ich habe festgestellt, dass diese Frage von Zeit zu Zeit auf Interesse stößt, daher werde ich ein wenig darauf eingehen.
Hintergrund zu Unit-Tests
Wenn Sie Unit-Tests durchführen, ist es wichtig, selbst zu definieren, was Sie als Arbeitseinheit betrachten. Grundsätzlich gilt: eine Extraktion Ihrer Codebasis, die mehrere Methoden oder Klassen enthalten kann oder nicht, die eine einzelne Funktionalität darstellen.
Oder wie in The art of Unit Testing, 2. Auflage von Roy Osherove , Seite 11 definiert:
Es ist wichtig zu erkennen, dass eine Arbeitseinheit normalerweise nicht nur eine Methode ist, sondern im Grunde genommen eine Methode, und danach wird sie von einer anderen Arbeitseinheit gekapselt.
Idealerweise sollten Sie für jede einzelne Arbeitseinheit eine Testmethode haben, damit Sie immer sofort sehen können, wo etwas schief geht. In diesem Beispiel gibt es eine grundlegende Methode,
getUserById()
die einen Benutzer zurückgibt, und es gibt insgesamt 3 Arbeitseinheiten.Die erste Arbeitseinheit sollte prüfen, ob bei gültigen und ungültigen Eingaben ein gültiger Benutzer zurückgegeben wird.
Alle Ausnahmen, die von der Datenquelle ausgelöst werden, müssen hier behandelt werden: Wenn kein Benutzer vorhanden ist, sollte ein Test durchgeführt werden, der zeigt, dass eine Ausnahme ausgelöst wird, wenn der Benutzer nicht gefunden werden kann. Ein Beispiel hierfür könnte das sein, das
IllegalArgumentException
mit der@Test(expected = IllegalArgumentException.class)
Anmerkung abgefangen wird .Sobald Sie alle Ihre Anwendungsfälle für diese grundlegende Arbeitseinheit bearbeitet haben, steigen Sie eine Ebene höher. Hier machen Sie genau das Gleiche, aber Sie behandeln nur die Ausnahmen, die von der Ebene direkt unter der aktuellen kommen. Dies hält Ihren Testcode gut strukturiert und ermöglicht es Ihnen, schnell durch die Architektur zu laufen, um herauszufinden, wo etwas schief geht, anstatt überall hüpfen zu müssen.
Behandlung der gültigen und fehlerhaften Eingabe eines Tests
An dieser Stelle sollte klar sein, wie wir mit diesen Ausnahmen umgehen werden. Es gibt zwei Arten von Eingaben: gültige Eingabe und fehlerhafte Eingabe (die Eingabe ist im engeren Sinne gültig, aber nicht korrekt).
Wenn Sie mit gültigen Eingaben arbeiten , legen Sie die implizite Erwartung fest, dass jeder Test, den Sie schreiben, funktioniert.
Ein solcher Methodenaufruf kann folgendermaßen aussehen :
existingUserById_ShouldReturn_UserObject
. Wenn diese Methode fehlschlägt (z. B. eine Ausnahme wird ausgelöst), wissen Sie, dass ein Fehler aufgetreten ist, und können mit dem Graben beginnen.Wenn Sie einen weiteren Test (
nonExistingUserById_ShouldThrow_IllegalArgumentException
) hinzufügen , der die fehlerhafte Eingabe verwendet und eine Ausnahme erwartet, können Sie feststellen, ob Ihre Methode mit falschen Eingaben das tut, was sie tun soll.TL; DR
Sie haben versucht, in Ihrem Test zwei Dinge zu tun: auf gültige und fehlerhafte Eingaben prüfen. Wenn Sie dies in zwei Methoden aufteilen, die jeweils eine Sache ausführen, erhalten Sie viel klarere Tests und einen viel besseren Überblick darüber, wo etwas schief geht.
Wenn Sie die geschichtete Arbeitseinheit berücksichtigen, können Sie auch die Anzahl der Tests reduzieren, die Sie für eine Ebene benötigen, die in der Hierarchie höher ist, da Sie nicht alles berücksichtigen müssen, was in den unteren Ebenen möglicherweise schief gelaufen ist: die Ebenen unterhalb der aktuellen Ebene sind eine virtuelle Garantie dafür, dass Ihre Abhängigkeiten funktionieren. Wenn etwas schief geht, befindet es sich in Ihrer aktuellen Ebene (vorausgesetzt, die unteren Ebenen werfen selbst keine Fehler).
quelle
expected
Anmerkung verwenden. Wenn Sie ein Szenario testen möchten, in dem Ihr Code fehlschlägt und Sie sehen möchten, ob der Fehler korrekt behandelt wird,expected
verwenden Sie Asserts, um festzustellen, ob er behoben wurde.throws IllegalArgumentException
Ihrem Test hinzu. Was Sie am Ende wollen, ist, dass Ihr Test rot wird, wenn es eine Ausnahme gibt. Rate mal? Sie müssen nicht schreibenfail()
. Wie @Jeroen Vannevel schrieb: "Wenn eine Ausnahme ausgelöst wird,Ich bin darauf gestoßen, weil SonarQubes Regel "squid: S2699" lautet: "Fügen Sie diesem Testfall mindestens eine Behauptung hinzu."
Ich hatte einen einfachen Test, dessen einziges Ziel es war, ohne Ausnahmen durchzugehen.
Betrachten Sie diesen einfachen Code:
Welche Art von Behauptung kann hinzugefügt werden, um diese Methode zu testen? Sicher, Sie können es versuchen, aber das ist nur Code Bloat.
Die Lösung kommt von JUnit selbst.
Wenn keine Ausnahme ausgelöst wird und Sie dieses Verhalten explizit veranschaulichen möchten, fügen Sie es einfach
expected
wie im folgenden Beispiel hinzu:Test.None.class
ist die Standardeinstellung für den erwarteten Wert.quelle
Mit AssertJ fließende Behauptungen 3.7.0 :
quelle
JUnit 5 (Jupiter) bietet drei Funktionen zum Überprüfen der Abwesenheit / Anwesenheit von Ausnahmen:
●
assertAll()
Behauptet , dass alle gelieferten
executables
nicht Ausnahmen werfen.
●
assertDoesNotThrow()
Behauptet , dass die Ausführung des
gelieferten
executable
/supplier
wirft nicht jede Art von Ausnahme .
Diese Funktion ist
seit JUnit 5.2.0 (29. April 2018) verfügbar .
●
assertThrows()
Behauptet , dass die Ausführung des mitgelieferten
executable
wirft eine Ausnahme der
expectedType
und gibt die Ausnahme .
Beispiel
quelle
Java 8 macht dies viel einfacher und Kotlin / Scala doppelt.
Wir können eine kleine Utility-Klasse schreiben
und dann wird Ihr Code einfach:
Wenn Sie keinen Zugriff auf Java-8 haben, würde ich eine schmerzhaft alte Java-Funktion verwenden: Aribitrary-Code-Blöcke und einen einfachen Kommentar
Und schließlich mit Kotlin, einer Sprache, in die ich mich kürzlich verliebt habe:
Obwohl es viel Raum gibt, genau damit herumzuspielen, wie Sie dies ausdrücken möchten, war ich immer ein Fan von fließenden Behauptungen .
Hinsichtlich
Dies ist im Prinzip richtig, aber in der Schlussfolgerung falsch.
Java erlaubt Ausnahmen für den Kontrollfluss. Dies erfolgt durch die JRE-Laufzeit selbst in APIs wie
Double.parseDouble
über aNumberFormatException
undPaths.get
über aInvalidPathException
.Wenn Sie eine Komponente geschrieben haben
Double.ParseDouble
, die Zahlenzeichenfolgen überprüft, z . B. mithilfe eines Regex, möglicherweise eines handgeschriebenen Parsers, oder wenn einige andere Domänenregeln eingebettet sind, die den Bereich eines Double auf etwas Bestimmtes beschränken, können Sie dies am besten testen Komponente? Ich denke, ein offensichtlicher Test wäre die Behauptung, dass beim Analysieren der resultierenden Zeichenfolge keine Ausnahme ausgelöst wird. Ich würde diesen Test entweder mit dem obigenassertDoesNotThrow
oder dem/*comment*/{code}
Block schreiben . Etwas wieIch würde empfehlen Ihnen , diesen Test zu parametrieren auf
input
VerwendungTheories
oderParameterized
so dass Sie leichter für andere Eingänge diesen Test erneut verwenden. Wenn Sie exotisch werden möchten, können Sie sich alternativ für ein Tool zur Testgenerierung entscheiden (und dies ). TestNG bietet eine bessere Unterstützung für parametrisierte Tests.Was ich besonders unangenehm finde, ist die Empfehlung der Verwendung
@Test(expectedException=IllegalArgumentException.class)
, diese Ausnahme ist gefährlich weit gefasst . Wenn sich Ihr Code so ändert, dass die Komponente unter dem Konstruktor des Tests vorhanden istif(constructorArgument <= 0) throw IllegalArgumentException()
, und Ihr Test 0 für dieses Argument lieferte, weil es praktisch war - und dies ist sehr häufig, da das gute Generieren von Testdaten ein überraschend schwieriges Problem ist -, dann ist Ihr Test wird grün-Balken sein, obwohl es nichts testet. Ein solcher Test ist schlimmer als nutzlos.quelle
Assert.assertThrows
überprüfen, ob ein Code eine Ausnahme auslöst.Wenn Sie das Pech haben, alle Fehler in Ihrem Code zu erkennen. Das kannst du dumm machen
quelle
Exception ex
sollte sein,= null;
bevor Sie es testen können.JUnit5 fügt genau zu diesem Zweck die assertAll () -Methode hinzu.
Quelle: JUnit 5 API
quelle
Obwohl dieser Beitrag jetzt 6 Jahre alt ist, hat sich in der Junit-Welt viel geändert. Mit Junit5 können Sie jetzt verwenden
Ex:
Hoffe, es wird Menschen helfen, die eine neuere Version von Junit5 verwenden
quelle
Awaitility
istuntilAsserted(ThrowingRunnable assertion)
. Das zu testende System löst derzeit eine bestimmte Ausnahme für das von mir bereitgestellte ThrowingRunnable aus, aber ich möchte ihm etwas Zeit geben, bis es damit aufhört. Wenn es jedoch eine andere Ausnahme auslösen würde, möchte ich, dass der Test sofort fehlschlägt.Wenn Sie testen möchten, ob Ihr Testziel die Ausnahme verwendet. Lassen Sie den Test einfach als (Mock Collaborator mit jMock2):
Der Test würde bestanden, wenn Ihr Ziel die ausgelöste Ausnahme verbraucht, andernfalls würde der Test fehlschlagen.
Wenn Sie Ihre Ausnahmekonsumlogik testen möchten, werden die Dinge komplexer. Ich schlage vor, den Verbrauch an einen Mitarbeiter zu delegieren, der verspottet werden könnte. Daher könnte der Test sein:
Aber manchmal ist es überarbeitet, wenn Sie es nur protokollieren möchten. In diesem Fall ist dieser Artikel ( http://java.dzone.com/articles/monitoring-declarative-transac , http://blog.novoj.net/2008/09/20/testing-aspect-pointcuts-is-there) -an-easy-way / ) kann helfen, wenn Sie in diesem Fall auf tdd bestehen.
quelle
Verwenden Sie assertNull (...)
quelle
assertNull
wird er auch nie ausgeführt. Der schnelle Leser hat jedoch den Eindruck, dass eine Behauptung aufgestellt wird, die den nicht werfenden Fall wirklich bestätigt. Mit anderen Worten: Wenn der catch-Block erreicht ist, ist die Ausnahme immer ungleich Null - sie kann daher durch eine einfache ersetzt werdenfail
.assertNull(e)
wird den Test als fehlgeschlagen melden, wie gesagte
kann nichtnull
imcatch
Block sein ... Mike das ist nur seltsame Programmierung: - /. .. ja zumindest verwendenfail()
wie Andreas sagtSie können erwarten, dass eine Ausnahme nicht ausgelöst wird, indem Sie eine Regel erstellen.
quelle
Dies ist möglicherweise nicht der beste Weg, stellt jedoch sicher, dass keine Ausnahme vom getesteten Codeblock ausgelöst wird.
quelle
Sie können dies tun, indem Sie eine @Rule verwenden und dann die Methode reportMissingExceptionWithMessage aufrufen, wie unten gezeigt: Dies ist Scala-Code.
quelle
private val
? Welche Sprache ist das? Ganz klar nicht Java; p Und bitte geben Sie keinen Code als Screenshot an, er wird nicht begrüßt.Folgendes führt den Test für alle aktivierten oder deaktivierten Ausnahmen nicht durch:
quelle
Sie können jede Art von eigenen Aussagen erstellen, die auf Aussagen von junit basieren:
Und testen:
Im Allgemeinen besteht die Möglichkeit, den Test in jedem Szenario und an jedem Ort, an dem er sinnvoll ist, sofort zu bestehen ("bla bla bla"). Verwenden Sie es beispielsweise in einem Try / Catch-Block, um zu scheitern, wenn im Testfall etwas ausgelöst wird:
Dies ist das Beispiel der Methode, die wir testen, vorausgesetzt, wir haben eine solche Methode, die unter bestimmten Umständen nicht fehlschlagen darf, aber fehlschlagen kann:
Die obige Methode ist ein einfaches Beispiel. Dies funktioniert jedoch in komplexen Situationen, in denen der Fehler nicht so offensichtlich ist. Es gibt die Importe:
quelle