Ich habe ein paar Methoden, die aufrufen sollten System.exit()
bestimmte Eingaben . Leider führt das Testen dieser Fälle dazu, dass JUnit beendet wird! Das Einfügen der Methodenaufrufe in einen neuen Thread scheint nicht zu helfen, da System.exit()
die JVM beendet wird, nicht nur der aktuelle Thread. Gibt es gemeinsame Muster, um damit umzugehen? Kann ich zum Beispiel einen Stub ersetzen System.exit()
?
[EDIT] Die betreffende Klasse ist eigentlich ein Befehlszeilentool, das ich in JUnit testen möchte. Vielleicht ist JUnit einfach nicht das richtige Werkzeug für den Job? Vorschläge für ergänzende Regressionstest-Tools sind willkommen (vorzugsweise etwas, das sich gut in JUnit und EclEmma integrieren lässt).
java
multithreading
unit-testing
junit
testability
Chris Conway
quelle
quelle
System.exit()
es schlecht ist - Ihr Programm sollte schnell fehlschlagen. Das Auslösen einer Ausnahme verlängert lediglich die Anwendung einer Anwendung in einem ungültigen Zustand in Situationen, in denen ein Entwickler das Programm beenden möchte, und führt zu falschen Fehlern.Antworten:
In der Tat schlägt Derkeiler.com vor:
System.exit()
?System.exit()
, dass die JVM tatsächlich beendet wird:Update Dezember 2012:
Will schlägt in den Kommentaren unter Verwendung von Systemregeln eine Sammlung von JUnit-Regeln (4.9+) zum Testen von Code vor, die verwendet werden
java.lang.System
.Dies wurde ursprünglich von Stefan Birkner in seiner Antwort im Dezember 2011 erwähnt.
Zum Beispiel:
quelle
System.exit
immer dann aufruft, wenn ein ungültiges Befehlszeilenargument angegeben wird.Die Bibliothek System Rules verfügt über eine JUnit-Regel namens ExpectedSystemExit. Mit dieser Regel können Sie Code testen, der System.exit (...) aufruft:
Vollständige Offenlegung: Ich bin der Autor dieser Bibliothek.
quelle
ExpectedSystemRule
ist natürlich nett; Das Problem ist, dass eine zusätzliche Bibliothek von Drittanbietern erforderlich ist, die in Bezug auf den tatsächlichen Nutzen nur sehr wenig bietet und JUnit-spezifisch ist.exit.checkAssertionAfterwards()
.Wie wäre es mit einem "ExitManager" in diese Methoden:
Der Produktionscode verwendet ExitManagerImpl und der Testcode verwendet ExitManagerMock und kann überprüfen, ob exit () aufgerufen wurde und mit welchem Exit-Code.
quelle
Sie können die
System.exit
Methode in einem JUnit-Test tatsächlich verspotten oder auslöschen.Mit JMockit können Sie beispielsweise schreiben (es gibt auch andere Möglichkeiten):
BEARBEITEN: Alternativer Test (unter Verwendung der neuesten JMockit-API), bei dem nach einem Aufruf von
System.exit(n)
: kein Code ausgeführt werden kann :quelle
exit
Aufruf ausgeführt wird (nicht, dass es wirklich ein Problem war, IMO).Runtime
kann verspottet werden, hört aberSystem.exit
zumindest in meinem Szenario nicht auf, Tests in Gradle auszuführen.Ein Trick, den wir in unserer Codebasis verwendet haben, bestand darin, den Aufruf von System.exit () in ein Runnable-Impl zu kapseln, das die betreffende Methode standardmäßig verwendet. Zum Unit-Test haben wir ein anderes Mock-Runnable festgelegt. Etwas wie das:
... und die JUnit-Testmethode ...
quelle
Erstellen Sie eine verspottbare Klasse, die System.exit () umschließt.
Ich stimme EricSchaefer zu . Aber wenn Sie ein gutes Spott-Framework wie Mockito verwenden eine einfache konkrete Klasse aus, ohne dass eine Schnittstelle und zwei Implementierungen erforderlich sind.
Beenden der Testausführung auf System.exit ()
Problem:
Ein Verspotteter
Sytem.exit()
beendet die Ausführung nicht. Das ist schlecht, wenn Sie das testen möchtenthing2
nicht ausgeführt wird.Lösung:
Sie sollten diesen Code wie von Martin vorgeschlagen umgestalten :
Und zwar
System.exit(status)
in der aufrufenden Funktion. Dies zwingt Sie dazu, alle IhreSystem.exit()
s an einem Ort in oder in der Nähe zu habenmain()
. Dies ist sauberer alsSystem.exit()
tief in Ihrer Logik zu rufen .Code
Verpackung:
Main:
Prüfung:
quelle
Ich mag einige der bereits gegebenen Antworten, aber ich wollte eine andere Technik demonstrieren, die oft nützlich ist, wenn Legacy-Code getestet wird. Gegebener Code wie:
Sie können ein sicheres Refactoring durchführen, um eine Methode zu erstellen, die den System.exit-Aufruf umschließt:
Dann können Sie eine Fälschung für Ihren Test erstellen, die den Exit überschreibt:
Dies ist eine generische Technik, um Testfälle durch Verhalten zu ersetzen, und ich verwende sie ständig, wenn ich alten Code umgestalte. Es ist normalerweise nicht der Ort, an dem ich das Ding verlassen werde, sondern ein Zwischenschritt, um den vorhandenen Code zu testen.
quelle
Ein kurzer Blick auf die API zeigt, dass System.exit eine Ausnahme auslösen kann, insb. wenn ein Sicherheitsmanager das Herunterfahren der VM verbietet. Vielleicht wäre eine Lösung, einen solchen Manager zu installieren.
quelle
Mit dem Java SecurityManager können Sie verhindern, dass der aktuelle Thread die Java-VM herunterfährt. Der folgende Code sollte tun, was Sie wollen:
quelle
Damit VonCs Antwort auf JUnit 4 ausgeführt werden kann, habe ich den Code wie folgt geändert
quelle
Sie können System.exit (..) testen, indem Sie die Runtime-Instanz ersetzen. ZB mit TestNG + Mockito:
quelle
Es gibt Umgebungen, in denen der zurückgegebene Exit-Code vom aufrufenden Programm verwendet wird (z. B. ERRORLEVEL in MS Batch). Wir haben Tests zu den wichtigsten Methoden, die dies in unserem Code tun, und unser Ansatz bestand darin, eine ähnliche SecurityManager-Überschreibung zu verwenden, wie sie in anderen Tests hier verwendet wurde.
Letzte Nacht habe ich eine kleine JAR mit Junit @ Rule-Annotationen zusammengestellt, um den Security Manager-Code auszublenden und Erwartungen basierend auf dem erwarteten Rückkehrcode hinzuzufügen. http://code.google.com/p/junitsystemrules/
quelle
Die meisten Lösungen werden
System.exit()
aufgerufen wirdSecurityManager
Daher sind die meisten Lösungen nicht für Situationen geeignet, in denen:
System.exit()
assertAll()
beispielsweise.Ich war mit den Einschränkungen, die durch die vorhandenen Lösungen in den anderen Antworten auferlegt wurden, nicht zufrieden und kam daher auf meine eigene Idee.
Die folgende Klasse stellt eine Methode bereit,
assertExits(int expectedStatus, Executable executable)
die bestätigt, dassSystem.exit()
sie mit einem angegebenenstatus
Wert aufgerufen wird, und der Test kann danach fortgesetzt werden. Es funktioniert genauso wie JUnit 5assertThrows
. Es respektiert auch einen vorhandenen Sicherheitsmanager.Es gibt noch ein Problem: Wenn der zu testende Code einen neuen Sicherheitsmanager installiert, der den durch den Test festgelegten Sicherheitsmanager vollständig ersetzt. Alle anderen
SecurityManager
mir bekannten Lösungen haben das gleiche Problem.Sie können die Klasse folgendermaßen verwenden:
Der Code kann bei Bedarf problemlos auf JUnit 4, TestNG oder ein anderes Framework portiert werden. Das einzige Framework-spezifische Element besteht darin, den Test nicht zu bestehen. Dies kann leicht in etwas Framework-unabhängiges geändert werden (außer einem Junit 4)
Rule
Es gibt Raum für Verbesserungen, zum Beispiel das Überladen
assertExits()
mit anpassbaren Nachrichten.quelle
Verwenden Sie
Runtime.exec(String command)
diese Option, um JVM in einem separaten Prozess zu starten.quelle
Es gibt ein kleines Problem mit der
SecurityManager
Lösung. Einige Methoden, wie z. B.JFrame.exitOnClose
callSecurityManager.checkExit
. In meiner Anwendung wollte ich nicht, dass dieser Aufruf fehlschlägt, also habe ich verwendetquelle
Das Aufrufen von System.exit () ist eine schlechte Vorgehensweise, es sei denn, es erfolgt in einem main (). Diese Methoden sollten eine Ausnahme auslösen, die letztendlich von Ihrem main () abgefangen wird, der dann System.exit mit dem entsprechenden Code aufruft.
quelle