Java: Jak przetestować metody wywołujące System.exit ()?

198

Mam kilka metod, które powinny wywoływać System.exit()określone dane wejściowe. Niestety testowanie tych przypadków powoduje zakończenie działania JUnit! Umieszczanie wywołań metod w nowym wątku nie wydaje się pomocne, ponieważ System.exit()kończy JVM, a nie tylko bieżący wątek. Czy istnieją jakieś wspólne wzorce radzenia sobie z tym? Na przykład, czy mogę zastąpić kod pośredniczącySystem.exit() ?

[EDYCJA] Ta klasa jest w rzeczywistości narzędziem wiersza poleceń, które próbuję przetestować w JUnit. Może JUnit po prostu nie jest odpowiednim narzędziem do pracy? Sugestie dotyczące uzupełniających narzędzi do testowania regresji są mile widziane (najlepiej coś, co dobrze integruje się z JUnit i EclEmma).

Chris Conway
źródło
2
Zastanawiam się, dlaczego funkcja miałaby wywoływać System.exit () ...
Thomas Owens,
2
Jeśli wywołujesz funkcję zamykającą aplikację. Na przykład, jeśli użytkownik spróbuje wykonać zadanie, do którego nie jest uprawniony do wykonywania więcej niż x razy z rzędu, wymusza się jego wycofanie z aplikacji.
Elie,
5
Nadal uważam, że w takim przypadku powinien istnieć lepszy sposób na powrót z aplikacji niż System.exit ().
Thomas Owens,
27
Jeśli testujesz main (), to sensowne jest wywołanie System.exit (). Mamy wymaganie, aby w przypadku błędu proces wsadowy zakończył się z wartością 1, a po pomyślnym - z wartością 0
Matthew Farwell
5
Nie zgadzam się ze wszystkimi, którzy twierdzą, że System.exit()jest zły - twój program powinien szybko zawieść. Zgłoszenie wyjątku przedłuża jedynie aplikację w niepoprawnym stanie w sytuacjach, w których programista chce wyjść i daje fałszywe błędy.
Sridhar Sarnobat

Odpowiedzi:

216

Rzeczywiście, Derkeiler.com sugeruje:

  • Dlaczego System.exit()?

Zamiast kończyć z System.exit (whatValue), dlaczego nie zgłosić niesprawdzonego wyjątku? Podczas normalnego użytkowania będzie dryfował aż do ostatniego łapacza JVM i wyłączy skrypt (chyba że zdecydujesz się go złapać gdzieś po drodze, co może być kiedyś przydatne).

W scenariuszu JUnit zostanie przechwycony przez platformę JUnit, która zgłosi, że test taki i taki nie powiódł się i płynnie przechodzi do następnego.

  • Zapobiegaj System.exit()faktycznemu wyjściu z JVM:

Spróbuj zmodyfikować TestCase, aby działał z menedżerem bezpieczeństwa, który uniemożliwia wywołanie System.exit, a następnie przechwyć wyjątek SecurityException.

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);
        }
    }
}

Aktualizacja z grudnia 2012 r .:

Will proponuje w komentarzach zastosowaniem zasady systemu , kolekcja JUnit (4.9+) zasady testowania kodu, który wykorzystuje java.lang.System.
Wspomniał o tym Stefan Birkner w swojej odpowiedzi z grudnia 2011 r.

System.exit(…)

Użyj ExpectedSystemExitreguły, aby sprawdzić, czy System.exit(…)to się nazywa.
Możesz także zweryfikować status wyjścia.

Na przykład:

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);
    }
}
VonC
źródło
4
Nie podobała mi się pierwsza odpowiedź, ale druga jest całkiem fajna - nie rozmawiałem z menedżerami bezpieczeństwa i zakładałem, że są bardziej skomplikowani. Jednak w jaki sposób testujesz menedżera bezpieczeństwa / mechanizm testowania.
Bill K
6
Upewnij się, że usuwanie jest wykonywane poprawnie, w przeciwnym razie test nie powiedzie się w programie uruchamiającym, takim jak Eclipse, ponieważ aplikacja JUnit nie może wyjść! :)
MetroidFan2002
6
Nie podoba mi się rozwiązanie za pomocą menedżera bezpieczeństwa. Wydaje mi się, że to hack, żeby to przetestować.
Nicolai Reuschling
6
Jeśli używasz junit 4.7 lub nowszego, pozwól bibliotece radzić sobie z przechwytywaniem twoich wywołań System.exit. Zasady systemowe - stefanbirkner.github.com/system-rules
Czy
6
„Zamiast kończyć z System.exit (whatValue), dlaczego nie zgłosić niesprawdzonego wyjątku?” - ponieważ używam struktury przetwarzania argumentów wiersza poleceń, która wywołuje się System.exitza każdym razem, gdy podany jest nieprawidłowy argument wiersza poleceń.
Adam Parkin,
106

Biblioteka System Rules ma regułę JUnit o nazwie ExpectedSystemExit. Dzięki tej regule możesz testować kod, który wywołuje System.exit (...):

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);
    }
}

Pełne ujawnienie: jestem autorem tej biblioteki.

Stefan Birkner
źródło
3
Idealny. Elegancki. Nie musiałem zmieniać ściegu mojego oryginalnego kodu ani bawić się z menedżerem bezpieczeństwa. To powinna być najlepsza odpowiedź!
Czy
2
To powinna być najlepsza odpowiedź. Zamiast niekończącej się debaty na temat prawidłowego korzystania z System.exit, od razu do rzeczy. Ponadto elastyczne rozwiązanie zgodne z samym JUnit, dzięki czemu nie musimy wymyślać koła ani zadzierać z menedżerem bezpieczeństwa.
L. Holanda
@LeoHolanda Czy to rozwiązanie nie cierpi z powodu tego samego „problemu”, który uzasadniałeś głosowanie negatywne mojej odpowiedzi? A co z użytkownikami TestNG?
Rogério
2
@LeoHolanda Masz rację; patrząc na implementację reguły, teraz widzę, że używa niestandardowego menedżera bezpieczeństwa, który zgłasza wyjątek, gdy pojawia się wezwanie do sprawdzenia „wyjścia z systemu”, co kończy test. Dodany przykładowy test w mojej odpowiedzi spełnia oba wymagania (poprawna weryfikacja wywołania / braku wywołania System.exit), BTW. ExpectedSystemRuleOczywiście użycie jest przyjemne; problem polega na tym, że wymaga dodatkowej biblioteki innej firmy, która zapewnia bardzo niewiele pod względem użyteczności w świecie rzeczywistym i jest specyficzna dla JUnit.
Rogério
2
Jest exit.checkAssertionAfterwards().
Stefan Birkner,
31

Co powiesz na wstrzyknięcie „ExitManager” do tych metod:

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
    }
}

Kod produkcyjny używa ExitManagerImpl, a kod testowy używa ExitManagerMock i może sprawdzić, czy wywołano metodę exit () i za pomocą którego kodu wyjścia.

EricSchaefer
źródło
1
+1 Dobre rozwiązanie. Łatwy do wdrożenia, jeśli używasz Springa, ponieważ ExitManager staje się prostym Komponentem. Należy tylko pamiętać, że należy upewnić się, że kod nie będzie kontynuowany po wywołaniu metody exitManager.exit (). Podczas testowania kodu za pomocą fałszywego narzędzia ExitManager kod nie zostanie zakończony po wywołaniu metody exitManager.exit.
Joman68
31

W rzeczywistości możesz wykpić lub wyeliminować System.exitmetodę w teście JUnit.

Na przykład, używając JMockit możesz pisać (są też inne sposoby):

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


EDYCJA: Test alternatywny (przy użyciu najnowszego interfejsu API JMockit), który nie pozwala na uruchomienie żadnego kodu po wywołaniu System.exit(n):

@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)");
}
Rogério
źródło
2
Zagłosuj przyczynę: Problem z tym rozwiązaniem polega na tym, że jeśli System.exit nie jest ostatnim wierszem w kodzie (tj. Wewnątrz warunku if, jeśli warunek), kod będzie nadal działać.
L. Holanda,
@LeoHolanda Dodano wersję testu, która uniemożliwia uruchomienie kodu po exitwywołaniu (nie żeby to naprawdę był problem, IMO).
Rogério,
Czy bardziej odpowiednie może być zastąpienie ostatniej System.out.println () instrukcją assert?
user515655,
„@Mocked („ exit ”)” wydaje się już nie działać z JMockit 1.43: „Wartość atrybutu jest niezdefiniowana dla typu adnotacji Mocked” Dokumenty: „Istnieją trzy różne drwiące adnotacje, których możemy użyć, kiedy deklarujemy fałszywe pola i parametry: @Mocked, które będą drwić ze wszystkich metod i konstruktorów we wszystkich istniejących i przyszłych instancjach fałszywej klasy (na czas trwania testów z jej użyciem); " jmockit.github.io/tutorial/Mocking.html#mocked
Thorsten Schöning
Czy rzeczywiście wypróbowałeś swoją sugestię? Dostaję: „java.lang.IllegalArgumentException: Klasa java.lang.System nie jest możliwa do wyśmiewania” Runtimemoże być wyśmiewana, ale nie zatrzymuje System.exitsię przynajmniej w moim scenariuszu z uruchomieniem Testów w gradle .
Thorsten Schöning
20

Jedną sztuczką, której użyliśmy w naszej bazie kodu, było zakodowanie wywołania System.exit () w implementacji Runnable, której domyślnie używa ta metoda. Aby przeprowadzić test jednostkowy, ustawiliśmy inny fałszywy Runnable. Coś takiego:

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(); 
}

... i metoda testowa JUnit ...

public void testFoo(){   
  final AtomicBoolean actionWasCalled = new AtomicBoolean(false);   
  fooObject.foo(new Runnable(){
    public void run(){
      actionWasCalled.set(true);
    }   
  });   
  assertTrue(actionWasCalled.get()); 
}
Scott Bale
źródło
Czy to właśnie nazywają zastrzykiem zależności?
Thomas Ahle,
2
Ten przykład, jak napisano, jest rodzajem iniekcji na wpół wypalonej zależności - zależność jest przekazywana do widocznej dla pakietu metody foo (za pomocą publicznej metody foo lub testu jednostkowego), ale główna klasa nadal koduje domyślną implementację Runnable.
Scott Bale,
6

Utwórz próbną klasę, która otacza System.exit ()

Zgadzam się z Ericsem Chaefer . Ale jeśli użyjesz dobrego frameworka, takiego jak Mockito, wystarczy prosta konkretna klasa, nie potrzebujesz interfejsu i dwóch implementacji.

Zatrzymywanie wykonywania testu w System.exit ()

Problem:

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

Wyśmiewany Sytem.exit()nie zakończy wykonywania. Jest to złe, jeśli chcesz przetestować, który thing2nie jest wykonywany.

Rozwiązanie:

Powinieneś zmienić ten kod zgodnie z sugestią Martina :

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

I wykonaj System.exit(status)funkcję wywoływania. To zmusza cię do trzymania wszystkich swoich System.exit()w jednym miejscu w lub w pobliżu main(). Jest to czystsze niż dzwonienie System.exit()głęboko w logice.

Kod

Obwoluta:

public class SystemExit {

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

Główny:

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;
    }
}

Test:

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);
    }
}
Arend przeciwko Reinersdorff
źródło
5

Podobają mi się niektóre z udzielonych odpowiedzi, ale chciałem zademonstrować inną technikę, która jest często przydatna podczas testowania starszego kodu. Podany kod, taki jak:

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

Możesz dokonać bezpiecznego refaktoryzacji, aby utworzyć metodę, która otacza wywołanie System.exit:

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

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

Następnie możesz utworzyć fałszywy test, który zastąpi wyjście:

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;
    }
}

Jest to ogólna technika zastępowania zachowania przypadkami testowymi i używam jej cały czas podczas refaktoryzacji starszego kodu. Zwykle nie jest to miejsce, w którym zamierzam zostawić coś, ale pośredni krok do przetestowania istniejącego kodu.

Jeffrey Fredrick
źródło
2
Ta technika nie zatrzymuje przepływu Conrol po wywołaniu funkcji exit (). Zamiast tego użyj wyjątku.
Andrea Francia,
3

Szybkie spojrzenie na interfejs API pokazuje, że System.exit może zgłosić wyjątek esp. jeśli sekuritymanager zabrania zamknięcia vm. Być może rozwiązaniem byłoby zainstalowanie takiego menedżera.

flolo
źródło
3

Możesz użyć java SecurityManager, aby zapobiec zamknięciu wirtualnej maszyny wirtualnej Java przez bieżący wątek. Poniższy kod powinien robić, co chcesz:

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);
Marc Nowakowski
źródło
Hm Dokumenty System.exit mówią, że zostanie wywołane checkExit (int), a nie checkPermission o nazwie = "exitVM". Zastanawiam się, czy powinienem zastąpić oba?
Chris Conway,
Prawdą jest, że nazwa uprawnienia to exitVM. (Kod statusu), tj. ExitVM.0 - przynajmniej w moim ostatnim teście na OSX.
Mark Derricutt
3

Aby odpowiedź VonC działała na JUnit 4, zmodyfikowałem kod w następujący sposób

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);
}
Jeow Li Huan
źródło
3

Możesz przetestować System.exit (..) z zastąpieniem wystąpienia Runtime. Np. Z 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);
    }
}
ursa
źródło
2

Istnieją środowiska, w których zwracany kod wyjścia jest używany przez program wywołujący (na przykład ERRORLEVEL w MS Batch). Mamy testy wokół głównych metod, które robią to w naszym kodzie, i naszym podejściem było użycie podobnego zastąpienia SecurityManager, jak w innych testach tutaj.

Ostatniej nocy zebrałem mały plik JAR za pomocą adnotacji Junit @Rule, aby ukryć kod menedżera bezpieczeństwa, a także dodać oczekiwania na podstawie oczekiwanego kodu powrotu. http://code.google.com/p/junitsystemrules/

Dan Watt
źródło
2

Większość rozwiązań będzie

  • zakończ test (metoda, a nie cały przebieg), System.exit()wywoływany jest moment
  • zignoruj ​​już zainstalowany SecurityManager
  • Czasami są dość specyficzne dla środowiska testowego
  • ograniczyć do użycia maksymalnie raz na przypadek testowy

Dlatego większość rozwiązań nie nadaje się do sytuacji, w których:

  • Weryfikację skutków ubocznych należy przeprowadzić po wezwaniu do System.exit()
  • Istniejący menedżer bezpieczeństwa jest częścią testów.
  • Używana jest inna platforma testowa.
  • Chcesz mieć wiele weryfikacji w jednym przypadku testowym. Może to nie być absolutnie zalecane, ale czasami może być bardzo wygodne, szczególnie w połączeniu z assertAll(), na przykład.

Nie byłem zadowolony z ograniczeń narzuconych przez istniejące rozwiązania przedstawione w innych odpowiedziach, dlatego sam coś wymyśliłem.

Następująca klasa udostępnia metodę, assertExits(int expectedStatus, Executable executable)która zapewnia, że System.exit()jest wywoływana z określoną statuswartością, a po niej można kontynuować test. Działa tak samo jak JUnit 5assertThrows . Szanuje również istniejącego menedżera bezpieczeństwa.

Pozostaje jeszcze jeden problem: gdy testowany kod instaluje nowego menedżera bezpieczeństwa, który całkowicie zastępuje menedżera bezpieczeństwa ustawionego przez test. Wszystkie inne SecurityManagerznane mi rozwiązania oparte są na tym samym problemie.

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;
        }
    }
}

Możesz użyć tej klasy w następujący sposób:

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

W razie potrzeby kod można łatwo przenieść do JUnit 4, TestNG lub dowolnego innego środowiska. Jedynym elementem specyficznym dla frameworka jest test zakończony niepowodzeniem. Można to łatwo zmienić na coś niezależnego od frameworka (innego niż Junit 4 Rule

Jest miejsce na ulepszenia, na przykład przeciążenie assertExits()dostosowywanymi komunikatami.

Christian Hujer
źródło
1

Służy Runtime.exec(String command)do uruchamiania JVM w oddzielnym procesie.

Aleksiej
źródło
3
Jak będziesz wchodzić w interakcje z testowaną klasą, jeśli jest ona w innym procesie niż test jednostkowy?
DNA
1
W 99% przypadków System.exit znajduje się w głównej metodzie. Dlatego wchodzisz w interakcje z nimi za pomocą argumentów wiersza poleceń (tj. Args).
L. Holanda,
1

Istnieje niewielki problem z SecurityManagerrozwiązaniem. Niektóre metody, takie jak JFrame.exitOnClosewywołanie SecurityManager.checkExit. W mojej aplikacji nie chciałem, aby to połączenie zakończyło się niepowodzeniem, więc skorzystałem

Class[] stack = getClassContext();
if (stack[1] != JFrame.class && !okToExit) throw new ExitException();
super.checkExit(status);
cayhorstmann
źródło
0

Wywołanie System.exit () jest złą praktyką, chyba że odbywa się to w main (). Metody te powinny zgłaszać wyjątek, który ostatecznie zostaje przechwycony przez twoją funkcję main (), która następnie wywołuje System.exit z odpowiednim kodem.


źródło
8
To jednak nie odpowiada na pytanie. Co jeśli testowana funkcja JEST ostatecznie główną metodą? Wywołanie System.exit () może być poprawne i ok z punktu widzenia projektu. Jak napisać dla niego testową skrzynkę?
Elie,
3
Nie powinieneś testować głównej metody, ponieważ główna metoda powinna po prostu wziąć argumenty, przekazać je do metody analizatora składni, a następnie uruchomić aplikację. Główna metoda do testowania nie powinna mieć logiki.
Thomas Owens,
5
@Elie: W tego typu pytaniach są dwie prawidłowe odpowiedzi. Jeden odpowiada na postawione pytanie, a drugi pyta, dlaczego pytanie było oparte. Oba rodzaje odpowiedzi dają lepsze zrozumienie, a zwłaszcza oba razem.
runaros