unitaria - testing en java
setUp/tearDown(@ Before/@ After) ¿por qué los necesitamos en JUnit? (6)
Aquí hay 3 buenas razones por qué. En resumen:
Algunas situaciones pueden preferir diferir la configuración de los dispositivos de prueba el mayor tiempo posible, justo antes de que se ejecute el caso de prueba.
Algunos casos de prueba pueden ser parte de una jerarquía profunda de herencia de casos de prueba. Puede ser preferible diferir la configuración de los dispositivos de prueba hasta que se complete la jerarquía completa de constructores.
Obtendrá mejores diagnósticos si el código de configuración falla en setUp () en lugar de si falla en el constructor.
1. Deferir configurar los dispositivos hasta justo antes del caso de prueba
Diseño para la usabilidad
http://www.artima.com/weblogs/viewpost.jsp?thread=70189
... Y como dijo Elliotte Rusty Harold, si vas a crear una nueva instancia de TestCase para cada método de prueba, "¿por qué molestarse con un método setUp ()?" Solo puede usar el constructor TestCase.
Escuché que Bruce Eckel señaló que hay una diferencia sutil entre crear tu accesorio en setUp () y crearlo en el constructor de TestCase. JUnit crea todas las instancias de TestCase por adelantado , y luego para cada instancia, llama a setup (), el método de prueba y tearDown () . En otras palabras, la diferencia sutil es que todos los constructores se invocan por lotes, mientras que el método setUp () se llama justo antes de cada método de prueba . Pero esto no parece ser una diferencia útil en la práctica.
2. Postergue la configuración de los dispositivos hasta que todos los casos de prueba sean instanciados
ETutorial''s Java Extreme Programming - 4.6 Configurar y desmontar
http://etutorials.org/Programming/Java+extreme+programming/Chapter+4.+JUnit/4.6+Set+Up+and+Tear+Down/
Quizás se pregunte por qué debería escribir un método setUp () en lugar de simplemente inicializar campos en el constructor de un caso de prueba . Después de todo, dado que se crea una nueva instancia del caso de prueba para cada uno de sus métodos de prueba, siempre se llama al constructor antes de setUp (). En la gran mayoría de los casos, puede usar el constructor en lugar de setUp () sin efectos secundarios.
En los casos donde su caso de prueba es parte de una jerarquía de herencia más profunda, es posible que desee posponer la inicialización del objeto hasta que las instancias de las clases derivadas [de prueba] estén totalmente construidas . Esta es una buena razón técnica por la cual es posible que desee usar setUp () en lugar de un constructor para la inicialización. Usar setUp () y tearDown () también es bueno para fines de documentación, simplemente porque puede hacer que el código sea más fácil de leer .
3. Mejor diagnóstico en caso de falla de configuración
Buenas prácticas de JUnit (JavaWorld)
http://www.javaworld.com/jw-12-2000/jw-1221-junit.html
Configurar un caso de prueba en el constructor no es una buena idea. ...
Imagínese [en el código donde la configuración se hace en el constructor del caso de prueba] que mientras se realiza la configuración, el código de instalación arroja una IllegalStateException. En respuesta, JUnit arrojaría un AssertionFailedError, que indica que el caso de prueba no se pudo crear una instancia. ...
Este seguimiento de la pila [de una excepción arrojada en el código de configuración en el constructor del caso de prueba] resulta bastante desinformativo; solo indica que el caso de prueba no pudo ser instanciado.
En lugar de configurar los datos en el constructor, realice la configuración de prueba anulando setUp (). Cualquier excepción lanzada dentro de setUp () se informa correctamente. ...
Este seguimiento de la pila [de una excepción arrojada en el método setUp () en lugar del constructor del caso de prueba] es mucho más informativo; muestra qué excepción se lanzó (IllegalStateException) y desde dónde. Eso hace que sea mucho más fácil explicar la falla de la instalación de prueba.
Creo que todos sabemos que setUp (@Before) se ejecutará antes de que se ejecute cualquier método de prueba y tearDown (@ After) después del método de prueba.
También sabemos que Junit creará una instancia de Prueba por método de prueba .
Mi pregunta es: ¿podemos simplemente mover el contenido del método setUp a Class Constructor y eliminar el método setUp? ¿Hay alguna razón específica para mantener el método setUp?
Creo que alguna razón debería gustarle lo siguiente:
- Si mueve los contenidos de @Antes de Constructor, está bien, ¿pero los contenidos de @Después pueden moverse?
- Las diferencias de Constructor y @ Before / @ After es que Constructor se debe usar para instanciar algunas para la clase, @ Before / @ After es para preparar recursos de casos de prueba.
En el trabajo, descubrimos algo bastante interesante que responde a su pregunta. Cuando ejecuta un conjunto de pruebas, especialmente un gran conjunto de pruebas (200+) JUnit comienza a usar MUCHA memoria, esto se debe a que TODAS las pruebas son instanciadas antes de ejecutar cualquier método de prueba real.
Nos topamos con una "pérdida de memoria" debido a esto porque usamos Spring para conectar algunos objetos JPA EntiryManager para nuestras pruebas de base de datos, esto se convirtió en MUCHOS objetos y mucha memoria ya la mitad de las pruebas obtuvimos excepciones de OutOfMemory .
En mi humilde opinión, la mejor práctica es usar setUp y TearDown para inyectar sus dependencias y anular todas las referencias de clase, esto hará que sus pruebas se ejecuten más rápido y le ahorrará mucho dolor de cabeza.
Espero que aprendas de nuestros errores :)
Este (viejo) artículo de mejores prácticas JUnit lo expresa así:
No use el constructor de caso de prueba para configurar un caso de prueba
Configurar un caso de prueba en el constructor no es una buena idea. Considerar:
public class SomeTest extends TestCase public SomeTest (String testName) { super (testName); // Perform test set-up } }
Imagine que mientras realiza la instalación, el código de instalación arroja una
IllegalStateException
. En respuesta, JUnit arrojaría unAssertionFailedError
, que indica que el caso de prueba no se pudo crear una instancia. Aquí hay un ejemplo del seguimiento de la pila resultante:
junit.framework.AssertionFailedError: Cannot instantiate test case: test1 at junit.framework.Assert.fail(Assert.java:143) at junit.framework.TestSuite.runTest(TestSuite.java:178) at junit.framework.TestCase.runBare(TestCase.java:129) at junit.framework.TestResult.protect(TestResult.java:100) at junit.framework.TestResult.runProtected(TestResult.java:117) at junit.framework.TestResult.run(TestResult.java:103) at junit.framework.TestCase.run(TestCase.java:120) at junit.framework.TestSuite.run(TestSuite.java, Compiled Code) at junit.ui.TestRunner2.run(TestRunner.java:429)
Este rastro de pila resulta bastante desinformativo; solo indica que el caso de prueba no pudo ser instanciado. No detalla la ubicación del error original o el lugar de origen. Esta falta de información hace que sea difícil deducir la causa subyacente de la excepción.
En lugar de configurar los datos en el constructor, realice la configuración de prueba anulando
setUp()
. Cualquier excepción lanzada dentro desetUp()
se informa correctamente. Compara este rastro de pila con el ejemplo anterior:
java.lang.IllegalStateException: Oops at bp.DTC.setUp(DTC.java:34) at junit.framework.TestCase.runBare(TestCase.java:127) at junit.framework.TestResult.protect(TestResult.java:100) at junit.framework.TestResult.runProtected(TestResult.java:117) at junit.framework.TestResult.run(TestResult.java:103) ...
Esta traza de pila es mucho más informativa; muestra qué excepción se lanzó (
IllegalStateException
) y desde dónde. Eso hace que sea mucho más fácil explicar la falla de la instalación de prueba.
La razón por la que necesita esto es que para muchas pruebas a menudo necesita inicializar el estado antes de cada prueba para que las pruebas puedan hacer suposiciones sobre el estado de inicio en el que se están ejecutando.
Supongamos que su clase de prueba se ajusta, digamos acceso a la base de datos. Después de cada prueba, desearía eliminar los cambios que sus pruebas hayan hecho en el DB: si no lo hizo, cada prueba se ejecuta contra una base de datos ligeramente modificada. Además, cualquier prueba dada puede ver un conjunto diferente de cambios si algún subconjunto de las pruebas anteriores ha fallado. Por ejemplo, supongamos que test1 hace una inserción, test2 verifica que está leyendo con precisión el tamaño de la tabla. Día 1, la prueba 1 falla y 0 es correcta. Día 2, prueba1 tiene éxito, y 1 es correcta?
BTW, junit también es compatible con @BeforeClass
si desea hacer una configuración global, y la configuración y los desmontajes son opcionales.
Un corredor personalizado como SpringJUnit4ClassRunner
puede necesitar ejecutar algunos códigos entre el constructor y el método @Before
. En este caso, el corredor puede inyectar alguna dependencia que los métodos @Before
necesitan. Pero la inyección de dependencia solo se puede ejecutar después de que se construye el objeto.