Tengo algunos métodos que deberían invocar System.exit()
ciertas entradas. Desafortunadamente, probar estos casos hace que JUnit termine. Poner las llamadas al método en un nuevo hilo no parece ayudar, ya queSystem.exit()
termina la JVM, no solo el hilo actual. ¿Hay algún patrón común para lidiar con esto? Por ejemplo, ¿puedo sustituir un trozo System.exit()
?
[EDITAR] La clase en cuestión es en realidad una herramienta de línea de comandos que estoy intentando probar dentro de JUnit. ¿Quizás JUnit simplemente no es la herramienta adecuada para el trabajo? Las sugerencias para herramientas de prueba de regresión complementarias son bienvenidas (preferiblemente algo que se integre bien con JUnit y EclEmma).
java
multithreading
unit-testing
junit
testability
Chris Conway
fuente
fuente
System.exit()
es malo: su programa debería fallar rápidamente. Lanzar una excepción simplemente prolonga una aplicación en un estado no válido en situaciones en las que un desarrollador quiere salir y dará errores espurios.Respuestas:
De hecho, Derkeiler.com sugiere:
System.exit()
?System.exit()
salir de la JVM:Actualización de diciembre de 2012:
Will propone en los comentarios usando las Reglas del sistema , una colección de reglas JUnit (4.9+) para probar el código que usa
java.lang.System
.Esto fue mencionado inicialmente por Stefan Birkner en su respuesta en diciembre de 2011.
Por ejemplo:
fuente
System.exit
siempre que se proporciona un argumento de línea de comandos no válido.La biblioteca System Lambda tiene un método.
catchSystemExit
Con esta regla puedes probar el código, que llama a System.exit (...):Para Java 5 a 7, la biblioteca System Rules tiene una regla JUnit llamada ExpectedSystemExit. Con esta regla, puede probar el código, que llama a System.exit (...):
Divulgación completa: soy el autor de ambas bibliotecas.
fuente
ExpectedSystemRule
es agradable, por supuesto; El problema es que requiere una biblioteca adicional de terceros que proporciona muy poco en términos de utilidad en el mundo real y es específica de JUnit.exit.checkAssertionAfterwards()
.¿Qué hay de inyectar un "ExitManager" en estos métodos:
El código de producción usa ExitManagerImpl y el código de prueba usa ExitManagerMock y puede verificar si se llamó a exit () y con qué código de salida.
fuente
En realidad, puedes burlarte o descartar el
System.exit
método, en una prueba JUnit.Por ejemplo, usando JMockit podría escribir (también hay otras formas):
EDITAR: Prueba alternativa (utilizando la última API de JMockit) que no permite que se ejecute ningún código después de una llamada a
System.exit(n)
:fuente
exit
llamada (no es que realmente fuera un problema, IMO).Runtime
se puede burlar, pero no se detieneSystem.exit
al menos en mi escenario ejecutando Pruebas en Gradle.Un truco que utilizamos en nuestra base de código fue hacer que la llamada a System.exit () se encapsulara en un impl Runnable, que el método en cuestión usaba por defecto. Para la prueba unitaria, establecemos un simulacro de Runnable diferente. Algo como esto:
... y el método de prueba JUnit ...
fuente
Cree una clase simulable que envuelva System.exit ()
Estoy de acuerdo con EricSchaefer . Pero si usa un buen marco de imitación como Mockito, una clase concreta simple es suficiente, no necesita una interfaz y dos implementaciones.
Detener la ejecución de la prueba en System.exit ()
Problema:
Una burla
Sytem.exit()
no terminará la ejecución. Esto es malo si quieres probar esothing2
no se ejecuta.Solución:
Deberías refactorizar este código como lo sugiere Martin :
Y hacer
System.exit(status)
en la función de llamada. Esto te obliga a tener todas tusSystem.exit()
direcciones en un solo lugar en o cercamain()
. Esto es más limpio que llamarSystem.exit()
profundamente dentro de su lógica.Código
Envoltura:
Principal:
Prueba:
fuente
Me gustan algunas de las respuestas ya dadas, pero quería demostrar una técnica diferente que a menudo es útil cuando se pone a prueba el código heredado. Código dado como:
Puede realizar una refactorización segura para crear un método que envuelva la llamada System.exit:
Luego puede crear una falsificación para su prueba que anule la salida:
Esta es una técnica genérica para sustituir el comportamiento de los casos de prueba, y lo uso todo el tiempo al refactorizar el código heredado. Por lo general, no es donde voy a dejar las cosas, sino un paso intermedio para probar el código existente.
fuente
Un vistazo rápido a la API muestra que System.exit puede lanzar una excepción especialmente. si un administrador de seguridad prohíbe el cierre de la máquina virtual. Tal vez una solución sería instalar dicho administrador.
fuente
Puede usar Java SecurityManager para evitar que el subproceso actual apague la máquina virtual Java. El siguiente código debe hacer lo que quieras:
fuente
Para que la respuesta de VonC se ejecute en JUnit 4, modifiqué el código de la siguiente manera
fuente
Puede probar System.exit (..) reemplazando la instancia de Runtime. Por ejemplo, con TestNG + Mockito:
fuente
Hay entornos en los que el programa de llamada utiliza el código de salida devuelto (como ERRORLEVEL en MS Batch). Tenemos pruebas en torno a los principales métodos que hacen esto en nuestro código, y nuestro enfoque ha sido utilizar una anulación de SecurityManager similar a la utilizada en otras pruebas aquí.
Anoche armé un pequeño JAR usando las anotaciones de Junit @Rule para ocultar el código del administrador de seguridad, así como para agregar expectativas basadas en el código de retorno esperado. http://code.google.com/p/junitsystemrules/
fuente
La mayoría de las soluciones lo harán
System.exit()
se llama el momentoSecurityManager
Por lo tanto, la mayoría de las soluciones no son adecuadas para situaciones en las que:
System.exit()
assertAll()
, por ejemplo.No estaba contento con las restricciones impuestas por las soluciones existentes presentadas en las otras respuestas y, por lo tanto, se me ocurrió algo por mi cuenta.
La siguiente clase proporciona un método
assertExits(int expectedStatus, Executable executable)
que afirma queSystem.exit()
se llama con unstatus
valor especificado , y la prueba puede continuar después. Funciona de la misma manera que JUnit 5assertThrows
. También respeta a un administrador de seguridad existente.Hay un problema restante: cuando el código bajo prueba instala un nuevo administrador de seguridad que reemplaza completamente al administrador de seguridad establecido por la prueba. Todas las otras
SecurityManager
soluciones basadas en mí que conozco sufren el mismo problema.Puedes usar la clase así:
El código se puede portar fácilmente a JUnit 4, TestNG o cualquier otro marco, si es necesario. El único elemento específico del marco está fallando la prueba. Esto se puede cambiar fácilmente a algo independiente del marco (que no sea un Junit 4
Rule
Hay margen de mejora, por ejemplo, sobrecarga de
assertExits()
mensajes personalizables.fuente
Use
Runtime.exec(String command)
para iniciar JVM en un proceso separado.fuente
Hay un pequeño problema con la
SecurityManager
solución. Algunos métodos, comoJFrame.exitOnClose
, también llamanSecurityManager.checkExit
. En mi solicitud, no quería que la llamada fallara, así que uséfuente
Llamar a System.exit () es una mala práctica, a menos que se haga dentro de un main (). Estos métodos deberían arrojar una excepción que, en última instancia, es captada por su main (), que luego llama a System.exit con el código apropiado.
fuente