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).
java
multithreading
unit-testing
junit
testability
Chris Conway
źródło
źródło
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.Odpowiedzi:
Rzeczywiście, Derkeiler.com sugeruje:
System.exit()
?System.exit()
faktycznemu wyjściu z JVM: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.
Na przykład:
źródło
System.exit
za każdym razem, gdy podany jest nieprawidłowy argument wiersza poleceń.Biblioteka System Rules ma regułę JUnit o nazwie ExpectedSystemExit. Dzięki tej regule możesz testować kod, który wywołuje System.exit (...):
Pełne ujawnienie: jestem autorem tej biblioteki.
źródło
ExpectedSystemRule
Oczywiś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.exit.checkAssertionAfterwards()
.Co powiesz na wstrzyknięcie „ExitManager” do tych metod:
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.
źródło
W rzeczywistości możesz wykpić lub wyeliminować
System.exit
metodę w teście JUnit.Na przykład, używając JMockit możesz pisać (są też inne sposoby):
EDYCJA: Test alternatywny (przy użyciu najnowszego interfejsu API JMockit), który nie pozwala na uruchomienie żadnego kodu po wywołaniu
System.exit(n)
:źródło
exit
wywołaniu (nie żeby to naprawdę był problem, IMO).Runtime
może być wyśmiewana, ale nie zatrzymujeSystem.exit
się przynajmniej w moim scenariuszu z uruchomieniem Testów w gradle .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:
... i metoda testowa JUnit ...
źródło
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:
Wyśmiewany
Sytem.exit()
nie zakończy wykonywania. Jest to złe, jeśli chcesz przetestować, którything2
nie jest wykonywany.Rozwiązanie:
Powinieneś zmienić ten kod zgodnie z sugestią Martina :
I wykonaj
System.exit(status)
funkcję wywoływania. To zmusza cię do trzymania wszystkich swoichSystem.exit()
w jednym miejscu w lub w pobliżumain()
. Jest to czystsze niż dzwonienieSystem.exit()
głęboko w logice.Kod
Obwoluta:
Główny:
Test:
źródło
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:
Możesz dokonać bezpiecznego refaktoryzacji, aby utworzyć metodę, która otacza wywołanie System.exit:
Następnie możesz utworzyć fałszywy test, który zastąpi wyjście:
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.
źródło
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.
źródło
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:
źródło
Aby odpowiedź VonC działała na JUnit 4, zmodyfikowałem kod w następujący sposób
źródło
Możesz przetestować System.exit (..) z zastąpieniem wystąpienia Runtime. Np. Z TestNG + Mockito:
źródło
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/
źródło
Większość rozwiązań będzie
System.exit()
wywoływany jest momentSecurityManager
Dlatego większość rozwiązań nie nadaje się do sytuacji, w których:
System.exit()
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, żeSystem.exit()
jest wywoływana z określonąstatus
wartoś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
SecurityManager
znane mi rozwiązania oparte są na tym samym problemie.Możesz użyć tej klasy w następujący sposób:
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.źródło
Służy
Runtime.exec(String command)
do uruchamiania JVM w oddzielnym procesie.źródło
Istnieje niewielki problem z
SecurityManager
rozwiązaniem. Niektóre metody, takie jakJFrame.exitOnClose
wywołanieSecurityManager.checkExit
. W mojej aplikacji nie chciałem, aby to połączenie zakończyło się niepowodzeniem, więc skorzystałemźródło
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