Java: Wie teste ich Methoden, die System.exit () aufrufen?


197

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).


2
Ich bin gespannt, warum eine Funktion jemals System.exit () aufrufen würde ...
Thomas Owens

2
Wenn Sie eine Funktion aufrufen, die die Anwendung beendet. Wenn der Benutzer beispielsweise versucht, eine Aufgabe auszuführen, ist er nicht berechtigt, mehr als x Mal hintereinander auszuführen, und erzwingen Sie, dass er die Anwendung verlässt.
Elie

5
Ich denke immer noch, dass es in diesem Fall eine schönere Möglichkeit geben sollte, von der Anwendung zurückzukehren, als System.exit ().
Thomas Owens

27
Wenn Sie main () testen, ist es absolut sinnvoll, System.exit () aufzurufen. Wir haben die Anforderung, dass bei einem Fehler ein Batch-Prozess mit 1 und bei Erfolg mit 0 beendet werden soll.
Matthew Farwell

5
Ich bin nicht einverstanden mit allen, die sagen, dass 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.
Sridhar Sarnobat

Antworten:


215

In der Tat schlägt Derkeiler.com vor:

  • Warum System.exit()?

Warum nicht eine ungeprüfte Ausnahme auslösen, anstatt mit System.exit (WhateverValue) zu beenden? Bei normaler Verwendung driftet es bis zum letzten Grabenfänger der JVM und fährt Ihr Skript herunter (es sei denn, Sie möchten es irgendwo auf dem Weg abfangen, was eines Tages nützlich sein könnte).

Im JUnit-Szenario wird es vom JUnit-Framework erfasst, das meldet, dass der eine oder andere Test fehlgeschlagen ist, und reibungslos zum nächsten übergeht.

  • Verhindern Sie System.exit(), dass die JVM tatsächlich beendet wird:

Versuchen Sie, den TestCase so zu ändern, dass er mit einem Sicherheitsmanager ausgeführt wird, der das Aufrufen von System.exit verhindert, und fangen Sie dann die SecurityException ab.

public class NoExitTestCase extends TestCase 
{

    protected static class ExitException extends SecurityException 
    {
        public final int status;
        public ExitException(int status) 
        {
            super("There is no escape!");
            this.status = status;
        }
    }

    private static class NoExitSecurityManager extends SecurityManager 
    {
        @Override
        public void checkPermission(Permission perm) 
        {
            // allow anything.
        }
        @Override
        public void checkPermission(Permission perm, Object context) 
        {
            // allow anything.
        }
        @Override
        public void checkExit(int status) 
        {
            super.checkExit(status);
            throw new ExitException(status);
        }
    }

    @Override
    protected void setUp() throws Exception 
    {
        super.setUp();
        System.setSecurityManager(new NoExitSecurityManager());
    }

    @Override
    protected void tearDown() throws Exception 
    {
        System.setSecurityManager(null); // or save and restore original
        super.tearDown();
    }

    public void testNoExit() throws Exception 
    {
        System.out.println("Printing works");
    }

    public void testExit() throws Exception 
    {
        try 
        {
            System.exit(42);
        } catch (ExitException e) 
        {
            assertEquals("Exit status", 42, e.status);
        }
    }
}

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.

System.exit(…)

Verwenden Sie die ExpectedSystemExitRegel, um zu überprüfen, ob sie System.exit(…)aufgerufen wird.
Sie können auch den Exit-Status überprüfen.

Zum Beispiel:

public void MyTest {
    @Rule
    public final ExpectedSystemExit exit = ExpectedSystemExit.none();

    @Test
    public void noSystemExit() {
        //passes
    }

    @Test
    public void systemExitWithArbitraryStatusCode() {
        exit.expectSystemExit();
        System.exit(0);
    }

    @Test
    public void systemExitWithSelectedStatusCode0() {
        exit.expectSystemExitWithStatus(0);
        System.exit(0);
    }
}

4
Die erste Antwort gefiel mir nicht, aber die zweite ist ziemlich cool - ich hatte mich nicht mit Sicherheitsmanagern angelegt und angenommen, dass sie komplizierter waren. Wie testen Sie jedoch den Sicherheitsmanager / Testmechanismus?
Bill K

6
Stellen Sie sicher, dass der Abriss ordnungsgemäß ausgeführt wird. Andernfalls schlägt Ihr Test in einem Runner wie Eclipse fehl, da die JUnit-Anwendung nicht beendet werden kann. :)
MetroidFan2002

6
Ich mag die Lösung mit dem Sicherheitsmanager nicht. Scheint mir ein Hack zu sein, nur um es zu testen.
Nicolai Reuschling

6
Wenn Sie Junit 4.7 oder höher verwenden, lassen Sie eine Bibliothek Ihre System.exit-Aufrufe erfassen. Systemregeln - stefanbirkner.github.com/system-rules
Will

6
"Anstatt mit System.exit (WhateverValue) zu beenden, können Sie eine ungeprüfte Ausnahme auslösen." - weil ich ein Framework zur Verarbeitung von Befehlszeilenargumenten verwende, das System.exitimmer dann aufruft, wenn ein ungültiges Befehlszeilenargument angegeben wird.
Adam Parkin

105

Die Bibliothek System Rules verfügt über eine JUnit-Regel namens ExpectedSystemExit. Mit dieser Regel können Sie Code testen, der System.exit (...) aufruft:

public void MyTest {
    @Rule
    public final ExpectedSystemExit exit = ExpectedSystemExit.none();

    @Test
    public void systemExitWithArbitraryStatusCode() {
        exit.expectSystemExit();
        //the code under test, which calls System.exit(...);
    }

    @Test
    public void systemExitWithSelectedStatusCode0() {
        exit.expectSystemExitWithStatus(0);
        //the code under test, which calls System.exit(0);
    }
}

Vollständige Offenlegung: Ich bin der Autor dieser Bibliothek.


3
Perfekt. Elegant. Ich musste keinen Stich meines ursprünglichen Codes ändern oder mit dem Sicherheitsmanager herumspielen. Dies sollte die beste Antwort sein!
Will

2
Dies sollte die beste Antwort sein. Anstelle einer endlosen Debatte über die ordnungsgemäße Verwendung von System.exit direkt auf den Punkt. Darüber hinaus eine flexible Lösung zur Einhaltung von JUnit selbst, sodass wir das Rad nicht neu erfinden oder den Sicherheitsmanager durcheinander bringen müssen.
L. Holanda

@LeoHolanda Leidet diese Lösung nicht unter dem gleichen "Problem", mit dem Sie eine Ablehnung meiner Antwort gerechtfertigt haben? Und was ist mit TestNG-Benutzern?
Rogério

2
@LeoHolanda Du hast recht; Wenn ich mir die Implementierung der Regel anschaue, sehe ich jetzt, dass sie einen benutzerdefinierten Sicherheitsmanager verwendet, der eine Ausnahme auslöst, wenn der Aufruf für die Prüfung "System-Exit" erfolgt, wodurch der Test beendet wird. Der in meiner Antwort hinzugefügte Beispieltest erfüllt beide Anforderungen (ordnungsgemäße Überprüfung mit System.exit wird aufgerufen / nicht aufgerufen), übrigens. Die Verwendung von ExpectedSystemRuleist 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.
Rogério

2
Es gibt exit.checkAssertionAfterwards().
Stefan Birkner

31

Wie wäre es mit einem "ExitManager" in diese Methoden:

public interface ExitManager {
    void exit(int exitCode);
}

public class ExitManagerImpl implements ExitManager {
    public void exit(int exitCode) {
        System.exit(exitCode);
    }
}

public class ExitManagerMock implements ExitManager {
    public bool exitWasCalled;
    public int exitCode;
    public void exit(int exitCode) {
        exitWasCalled = true;
        this.exitCode = exitCode;
    }
}

public class MethodsCallExit {
    public void CallsExit(ExitManager exitManager) {
        // whatever
        if (foo) {
            exitManager.exit(42);
        }
        // whatever
    }
}

Der Produktionscode verwendet ExitManagerImpl und der Testcode verwendet ExitManagerMock und kann überprüfen, ob exit () aufgerufen wurde und mit welchem ​​Exit-Code.


1
+1 Schöne Lösung. Auch einfach zu implementieren, wenn Sie Spring verwenden, da der ExitManager zu einer einfachen Komponente wird. Beachten Sie jedoch, dass Sie sicherstellen müssen, dass Ihr Code nach dem Aufruf von exitManager.exit () nicht mehr ausgeführt wird. Wenn Ihr Code mit einem nachgebildeten ExitManager getestet wird, wird der Code nach dem Aufruf von exitManager.exit nicht beendet.
Joman68

31

Sie können die System.exitMethode in einem JUnit-Test tatsächlich verspotten oder auslöschen.

Mit JMockit können Sie beispielsweise schreiben (es gibt auch andere Möglichkeiten):

@Test
public void mockSystemExit(@Mocked("exit") System mockSystem)
{
    // Called by code under test:
    System.exit(); // will not exit the program
}


BEARBEITEN: Alternativer Test (unter Verwendung der neuesten JMockit-API), bei dem nach einem Aufruf von System.exit(n): kein Code ausgeführt werden kann :

@Test(expected = EOFException.class)
public void checkingForSystemExitWhileNotAllowingCodeToContinueToRun() {
    new Expectations(System.class) {{ System.exit(anyInt); result = new EOFException(); }};

    // From the code under test:
    System.exit(1);
    System.out.println("This will never run (and not exit either)");
}

2
Abstimmungsgrund: Das Problem bei dieser Lösung besteht darin, dass der Code weiterhin ausgeführt wird, wenn System.exit nicht die letzte Zeile im Code ist (dh innerhalb der if-Bedingung).
L. Holanda

@LeoHolanda Es wurde eine Version des Tests hinzugefügt, die verhindert, dass Code nach dem exitAufruf ausgeführt wird (nicht, dass es wirklich ein Problem war, IMO).
Rogério

Könnte es angemessener sein, die letzte System.out.println () durch eine assert-Anweisung zu ersetzen?
user515655

"@Mocked (" exit ")" scheint mit JMockit 1.43 nicht mehr zu funktionieren: "Der Attributwert ist für den Annotationstyp Mocked undefiniert." Die Dokumente: "Es gibt drei verschiedene Mocking-Annotationen, die wir beim Deklarieren von Mock-Feldern und verwenden können Parameter: @Mocked, der alle Methoden und Konstruktoren aller vorhandenen und zukünftigen Instanzen einer verspotteten Klasse verspottet (für die Dauer der Tests, die sie verwenden); " jmockit.github.io/tutorial/Mocking.html#mocked
Thorsten Schöning

Haben Sie Ihren Vorschlag tatsächlich ausprobiert? Ich erhalte: "java.lang.IllegalArgumentException: Klasse java.lang.System ist nicht verspottbar" Runtimekann verspottet werden, hört aber System.exitzumindest in meinem Szenario nicht auf, Tests in Gradle auszuführen.
Thorsten Schöning

20

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:

private static final Runnable DEFAULT_ACTION = new Runnable(){
  public void run(){
    System.exit(0);
  }
};

public void foo(){ 
  this.foo(DEFAULT_ACTION);
}

/* package-visible only for unit testing */
void foo(Runnable action){   
  // ...some stuff...   
  action.run(); 
}

... und die JUnit-Testmethode ...

public void testFoo(){   
  final AtomicBoolean actionWasCalled = new AtomicBoolean(false);   
  fooObject.foo(new Runnable(){
    public void run(){
      actionWasCalled.set(true);
    }   
  });   
  assertTrue(actionWasCalled.get()); 
}

Nennen sie das Abhängigkeitsinjektion?
Thomas Ahle

2
Dieses geschriebene Beispiel ist eine Art halbgebackene Abhängigkeitsinjektion - die Abhängigkeit wird an die paket-sichtbare foo-Methode übergeben (entweder durch die öffentliche foo-Methode oder den Komponententest), aber die Hauptklasse codiert die Standard-Runnable-Implementierung weiterhin fest.
Scott Bale

6

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:

// do thing1
if(someCondition) {
    System.exit(1);
}
// do thing2
System.exit(0)

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 :

// do thing1
if(someCondition) {
    return 1;
}
// do thing2
return 0;

Und zwar System.exit(status)in der aufrufenden Funktion. Dies zwingt Sie dazu, alle Ihre System.exit()s an einem Ort in oder in der Nähe zu haben main(). Dies ist sauberer als System.exit()tief in Ihrer Logik zu rufen .

Code

Verpackung:

public class SystemExit {

    public void exit(int status) {
        System.exit(status);
    }
}

Main:

public class Main {

    private final SystemExit systemExit;


    Main(SystemExit systemExit) {
        this.systemExit = systemExit;
    }


    public static void main(String[] args) {
        SystemExit aSystemExit = new SystemExit();
        Main main = new Main(aSystemExit);

        main.executeAndExit(args);
    }


    void executeAndExit(String[] args) {
        int status = execute(args);
        systemExit.exit(status);
    }


    private int execute(String[] args) {
        System.out.println("First argument:");
        if (args.length == 0) {
            return 1;
        }
        System.out.println(args[0]);
        return 0;
    }
}

Prüfung:

public class MainTest {

    private Main       main;

    private SystemExit systemExit;


    @Before
    public void setUp() {
        systemExit = mock(SystemExit.class);
        main = new Main(systemExit);
    }


    @Test
    public void executeCallsSystemExit() {
        String[] emptyArgs = {};

        // test
        main.executeAndExit(emptyArgs);

        verify(systemExit).exit(1);
    }
}

5

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:

public class Foo {
  public void bar(int i) {
    if (i < 0) {
      System.exit(i);
    }
  }
}

Sie können ein sicheres Refactoring durchführen, um eine Methode zu erstellen, die den System.exit-Aufruf umschließt:

public class Foo {
  public void bar(int i) {
    if (i < 0) {
      exit(i);
    }
  }

  void exit(int i) {
    System.exit(i);
  }
}

Dann können Sie eine Fälschung für Ihren Test erstellen, die den Exit überschreibt:

public class TestFoo extends TestCase {

  public void testShouldExitWithNegativeNumbers() {
    TestFoo foo = new TestFoo();
    foo.bar(-1);
    assertTrue(foo.exitCalled);
    assertEquals(-1, foo.exitValue);
  }

  private class TestFoo extends Foo {
    boolean exitCalled;
    int exitValue;
    void exit(int i) {
      exitCalled = true;
      exitValue = i;
    }
}

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.


2
Diese Technik stoppt den Steuerungsfluss nicht, wenn exit () aufgerufen wurde. Verwenden Sie stattdessen eine Ausnahme.
Andrea Francia

3

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.


3

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:

SecurityManager securityManager = new SecurityManager() {
    public void checkPermission(Permission permission) {
        if ("exitVM".equals(permission.getName())) {
            throw new SecurityException("System.exit attempted and blocked.");
        }
    }
};
System.setSecurityManager(securityManager);

Hm. In den System.exit-Dokumenten wird ausdrücklich angegeben, dass checkExit (int) aufgerufen wird, nicht checkPermission mit name = "exitVM". Ich frage mich, ob ich beide überschreiben soll.
Chris Conway

Der Berechtigungsname scheint tatsächlich exitVM (Statuscode) zu sein, dh exitVM.0 - zumindest in meinem letzten Test unter OSX.
Mark Derricutt

3

Damit VonCs Antwort auf JUnit 4 ausgeführt werden kann, habe ich den Code wie folgt geändert

protected static class ExitException extends SecurityException {
    private static final long serialVersionUID = -1982617086752946683L;
    public final int status;

    public ExitException(int status) {
        super("There is no escape!");
        this.status = status;
    }
}

private static class NoExitSecurityManager extends SecurityManager {
    @Override
    public void checkPermission(Permission perm) {
        // allow anything.
    }

    @Override
    public void checkPermission(Permission perm, Object context) {
        // allow anything.
    }

    @Override
    public void checkExit(int status) {
        super.checkExit(status);
        throw new ExitException(status);
    }
}

private SecurityManager securityManager;

@Before
public void setUp() {
    securityManager = System.getSecurityManager();
    System.setSecurityManager(new NoExitSecurityManager());
}

@After
public void tearDown() {
    System.setSecurityManager(securityManager);
}

3

Sie können System.exit (..) testen, indem Sie die Runtime-Instanz ersetzen. ZB mit TestNG + Mockito:

public class ConsoleTest {
    /** Original runtime. */
    private Runtime originalRuntime;

    /** Mocked runtime. */
    private Runtime spyRuntime;

    @BeforeMethod
    public void setUp() {
        originalRuntime = Runtime.getRuntime();
        spyRuntime = spy(originalRuntime);

        // Replace original runtime with a spy (via reflection).
        Utils.setField(Runtime.class, "currentRuntime", spyRuntime);
    }

    @AfterMethod
    public void tearDown() {
        // Recover original runtime.
        Utils.setField(Runtime.class, "currentRuntime", originalRuntime);
    }

    @Test
    public void testSystemExit() {
        // Or anything you want as an answer.
        doNothing().when(spyRuntime).exit(anyInt());

        System.exit(1);

        verify(spyRuntime).exit(1);
    }
}

2

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/


2

Die meisten Lösungen werden

  • Beenden Sie den Test (Methode, nicht den gesamten Lauf), sobald der Moment System.exit()aufgerufen wird
  • ignoriere eine bereits installierte SecurityManager
  • Seien Sie manchmal sehr spezifisch für ein Test-Framework
  • Beschränken Sie die Verwendung auf maximal einmal pro Testfall

Daher sind die meisten Lösungen nicht für Situationen geeignet, in denen:

  • Die Überprüfung der Nebenwirkungen ist nach dem Anruf bei durchzuführen System.exit()
  • Ein vorhandener Sicherheitsmanager ist Teil des Tests.
  • Ein anderes Testframework wird verwendet.
  • Sie möchten mehrere Überprüfungen in einem einzigen Testfall durchführen. Dies kann strengstens nicht empfohlen werden, kann aber manchmal sehr praktisch sein, insbesondere in Kombination mit 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, dass System.exit()sie mit einem angegebenen statusWert 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 SecurityManagermir bekannten Lösungen haben das gleiche Problem.

import java.security.Permission;

import static java.lang.System.getSecurityManager;
import static java.lang.System.setSecurityManager;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;

public enum ExitAssertions {
    ;

    public static <E extends Throwable> void assertExits(final int expectedStatus, final ThrowingExecutable<E> executable) throws E {
        final SecurityManager originalSecurityManager = getSecurityManager();
        setSecurityManager(new SecurityManager() {
            @Override
            public void checkPermission(final Permission perm) {
                if (originalSecurityManager != null)
                    originalSecurityManager.checkPermission(perm);
            }

            @Override
            public void checkPermission(final Permission perm, final Object context) {
                if (originalSecurityManager != null)
                    originalSecurityManager.checkPermission(perm, context);
            }

            @Override
            public void checkExit(final int status) {
                super.checkExit(status);
                throw new ExitException(status);
            }
        });
        try {
            executable.run();
            fail("Expected System.exit(" + expectedStatus + ") to be called, but it wasn't called.");
        } catch (final ExitException e) {
            assertEquals(expectedStatus, e.status, "Wrong System.exit() status.");
        } finally {
            setSecurityManager(originalSecurityManager);
        }
    }

    public interface ThrowingExecutable<E extends Throwable> {
        void run() throws E;
    }

    private static class ExitException extends SecurityException {
        final int status;

        private ExitException(final int status) {
            this.status = status;
        }
    }
}

Sie können die Klasse folgendermaßen verwenden:

    @Test
    void example() {
        assertExits(0, () -> System.exit(0)); // succeeds
        assertExits(1, () -> System.exit(1)); // succeeds
        assertExits(2, () -> System.exit(1)); // fails
    }

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.


1

Verwenden Sie Runtime.exec(String command)diese Option, um JVM in einem separaten Prozess zu starten.


3
Wie werden Sie mit der zu testenden Klasse interagieren, wenn sie sich in einem vom Komponententest getrennten Prozess befindet?
DNA

1
In 99% der Fälle befindet sich System.exit in einer Hauptmethode. Daher interagieren Sie mit ihnen über Befehlszeilenargumente (dh Argumente).
L. Holanda

1

Es gibt ein kleines Problem mit der SecurityManagerLösung. Einige Methoden, wie z. B. JFrame.exitOnClosecall SecurityManager.checkExit. In meiner Anwendung wollte ich nicht, dass dieser Aufruf fehlschlägt, also habe ich verwendet

Class[] stack = getClassContext();
if (stack[1] != JFrame.class && !okToExit) throw new ExitException();
super.checkExit(status);

0

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.


8
Das beantwortet die Frage jedoch nicht. Was ist, wenn die getestete Funktion letztendlich die Hauptmethode ist? Das Aufrufen von System.exit () kann daher aus gestalterischer Sicht gültig und in Ordnung sein. Wie schreibt man einen Testfall dafür?
Elie

3
Sie sollten die Hauptmethode nicht testen müssen, da die Hauptmethode nur Argumente annehmen, diese an eine Parser-Methode übergeben und dann die Anwendung starten sollte. Die zu testende Hauptmethode sollte keine Logik enthalten.
Thomas Owens

5
@Elie: Bei solchen Fragen gibt es zwei gültige Antworten. Einer beantwortete die gestellte Frage und einer fragte, warum die Frage begründet war. Beide Arten von Antworten vermitteln ein besseres Verständnis, insbesondere beide zusammen.
Runaros
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.