unitarias tutorial test software pruebas español ejemplos configuracion java junit

java - tutorial - pruebas unitarias



Mejor práctica: Inicializa los campos de la clase JUnit en setUp() o en la declaración? (8)

Además de la respuesta de Alex B.

Incluso se requiere el uso del método setUp para crear instancias de recursos en un estado determinado. Hacer esto en el constructor no es solo una cuestión de tiempos, sino que debido a la forma en que JUnit ejecuta las pruebas, cada estado de prueba se borrará después de ejecutar uno.

JUnit primero crea instancias de testClass para cada método de prueba y comienza a ejecutar las pruebas después de que se crea cada instancia. Antes de ejecutar el método de prueba, se ejecuta su método de configuración, en el que se puede preparar algún estado.

Si el estado de la base de datos se creara en el constructor, todas las instancias crearían una instancia del estado de la base de datos justo después una de la otra, antes de ejecutar cada prueba. A partir de la segunda prueba, las pruebas se ejecutarían con un estado sucio.

Ciclo de vida de JUnits:

  1. Cree una instancia de clase de prueba diferente para cada método de prueba
  2. Repita para cada instancia de clase de prueba: llame a configuración + llame al método de prueba

Con algunos registros en una prueba con dos métodos de prueba obtienes: (number is the hashcode)

  • Creando una nueva instancia: 5718203
  • Creando una nueva instancia: 5947506
  • Configuración: 5718203
  • TestOne: 5718203
  • Configuración: 5947506
  • Test Two: 5947506

¿Debería inicializar los campos de clase en una declaración como esta?

public class SomeTest extends TestCase { private final List list = new ArrayList(); public void testPopulateList() { // Add stuff to the list // Assert the list contains what I expect } }

O en setUp () como este?

public class SomeTest extends TestCase { private List list; @Override protected void setUp() throws Exception { super.setUp(); this.list = new ArrayList(); } public void testPopulateList() { // Add stuff to the list // Assert the list contains what I expect } }

Tiendo a usar el primer formulario porque es más conciso y me permite usar los campos finales. Si no necesito usar el método setUp () para la configuración, ¿debería seguir usándolo y por qué?

Aclaración: JUnit instanciará la clase de prueba una vez por método de prueba. Eso significa que la list se creará una vez por prueba, independientemente de dónde la declare. También significa que no hay dependencias temporales entre las pruebas. Entonces parece que no hay ventajas al usar setUp (). Sin embargo, las preguntas frecuentes de JUnit tienen muchos ejemplos que inicializan una colección vacía en setUp (), por lo que creo que debe haber un motivo.


Como cada prueba se ejecuta de forma independiente, con una nueva instancia del objeto, no tiene mucho sentido que el objeto Test tenga ningún estado interno, excepto el compartido entre setUp() y una prueba individual y tearDown() . Esta es una de las razones (además de las razones que otros dieron) de que es bueno usar el método setUp() .

Nota: ¡es una mala idea que un objeto de prueba JUnit mantenga el estado estático! Si utiliza variables estáticas en sus pruebas para fines distintos de seguimiento o diagnóstico, está invalidando parte del propósito de JUnit, que es que las pruebas se pueden ejecutar en cualquier orden, y cada prueba se ejecuta con una estado fresco y limpio.

Las ventajas de utilizar setUp() es que no tiene que cortar y pegar el código de inicialización en cada método de prueba y que no tiene un código de configuración de prueba en el constructor. En tu caso, hay poca diferencia. Solo crear una lista vacía se puede hacer de manera segura como se muestra en el constructor, ya que es una inicialización trivial. Sin embargo, como usted y otros han señalado, todo lo que posiblemente arroje una Exception debe hacer en setUp() para que obtenga el volcado de pila de diagnóstico si falla.

En su caso, cuando solo está creando una lista vacía, yo haría lo mismo que está sugiriendo: Asigne la nueva lista en el momento de la declaración. Especialmente porque de esta manera tienes la opción de marcarlo como final si esto tiene sentido para tu clase de prueba.


Empecé a excavar y encontré una ventaja potencial de usar setUp() . Si se lanzan excepciones durante la ejecución de setUp() , JUnit imprimirá un rastro de pila muy útil. Por otro lado, si se lanza una excepción durante la construcción del objeto, el mensaje de error simplemente dice que JUnit no pudo crear una instancia del caso de prueba y usted no ve el número de línea donde ocurrió la falla, probablemente porque JUnit usa la reflexión para crear una instancia de la prueba clases

Nada de esto se aplica al ejemplo de crear una colección vacía, ya que eso nunca arrojará, pero es una ventaja del método setUp() .


En JUnit 3, sus inicializadores de campo se ejecutarán una vez por cada método de prueba antes de ejecutar cualquier prueba . Siempre que los valores de campo sean pequeños en la memoria, tome poco tiempo de configuración y no afecte el estado global, el uso de inicializadores de campo es técnicamente correcto. Sin embargo, si no se mantienen, puede terminar consumiendo mucha memoria o tiempo configurando sus campos antes de ejecutar la primera prueba, y posiblemente hasta quedarse sin memoria. Por esta razón, muchos desarrolladores siempre establecen valores de campo en el método setUp (), donde siempre es seguro, incluso cuando no es estrictamente necesario.

Tenga en cuenta que en JUnit 4, la inicialización del objeto de prueba ocurre justo antes de ejecutar la prueba, por lo que el uso de inicializadores de campo es más seguro y el estilo recomendado.


En JUnit 4:

  • Para Class Under Test , @Before en un método @Before , para detectar fallas.
  • Para otras clases , inicializa en la declaración ...
    • ... por brevedad, y para marcar campos final , exactamente como se indica en la pregunta,
    • ... a menos que sea una inicialización compleja que podría fallar, en cuyo caso use @Before para detectar fallas.
  • Para el estado global (especialmente la inicialización lenta , como una base de datos), use @BeforeClass , pero tenga cuidado con las dependencias entre las pruebas.
  • La inicialización de un objeto utilizado en una sola prueba debería realizarse en el método de prueba mismo.

Inicializar en un método @Before o método de prueba le permite obtener mejores informes de errores en fallas. Esto es especialmente útil para crear instancias de Class Under Test (que puede romper), pero también es útil para llamar sistemas externos, como el acceso al sistema de archivos ("archivo no encontrado") o conectarse a una base de datos ("connection refused").

Es aceptable tener un estándar simple y siempre usar @Before (borrar errores pero detallado) o siempre inicializar en la declaración (conciso pero da errores confusos), ya que las reglas complejas de codificación son difíciles de seguir, y esto no es un gran problema.

Inicializar en setUp es una reliquia de JUnit 3, donde todas las instancias de prueba se inicializaron con entusiasmo, lo que causa problemas (velocidad, memoria, agotamiento de recursos) si realiza costosas inicializaciones. Por lo tanto, la mejor práctica era realizar una costosa inicialización en setUp , que solo se ejecutó cuando se ejecutó la prueba. Esto ya no se aplica, por lo que es mucho menos necesario usar setUp .

Esto resume varias otras respuestas que entierran al lede, especialmente por Craig P. Motlin (pregunta en sí y auto respuesta), Moss Collum (clase bajo prueba) y dsaff.


En su caso (crear una lista) no hay diferencia en la práctica. Pero, en general, es mejor usar setUp (), porque eso ayudará a Junit a informar Excepciones correctamente. Si se produce una excepción en el constructor / inicializador de una prueba, es una falla de prueba. Sin embargo, si se produce una excepción durante la configuración, es natural pensar que es un problema al configurar la prueba, y Junit informa adecuadamente.


Prefiero la legibilidad primero que la mayoría de las veces no usa el método de configuración. Hago una excepción cuando una operación de configuración básica toma mucho tiempo y se repite dentro de cada prueba.
En ese punto, muevo esa funcionalidad a un método de configuración usando la anotación @BeforeClass (optimice más adelante).

Ejemplo de optimización utilizando el método de configuración @BeforeClass : uso dbunit para algunas pruebas funcionales de base de datos. El método de configuración es responsable de poner la base de datos en un estado conocido (muy lento ... 30 segundos - 2 minutos dependiendo de la cantidad de datos). @BeforeClass estos datos en el método de configuración anotado con @BeforeClass y luego ejecuto 10-20 pruebas contra el mismo conjunto de datos en lugar de volver a cargar / inicializar la base de datos dentro de cada prueba.

Usar Junit 3.8 (extendiendo TestCase como se muestra en su ejemplo) requiere escribir un poco más de código que simplemente agregar una anotación, pero la "ejecución una vez antes de la configuración de clase" todavía es posible.


Si te estás preguntando específicamente sobre los ejemplos en las preguntas frecuentes de JUnit, como la plantilla de prueba básica , creo que la mejor práctica que se muestra allí es que la clase bajo prueba se debe instanciar en tu método setUp (o en un método de prueba) .

Cuando los ejemplos de JUnit crean un ArrayList en el método setUp, todos prueban el comportamiento de ese ArrayList, con casos como testIndexOutOfBoundException, testEmptyCollection y similares. La perspectiva es que alguien escribe una clase y se asegura de que funcione correctamente.

Probablemente deberías hacer lo mismo cuando pruebes tus propias clases: crea tu objeto en setUp o en un método de prueba, para que puedas obtener resultados razonables si lo rompes más tarde.

Por otro lado, si utiliza una clase de colección de Java (u otra clase de biblioteca, para el caso) en su código de prueba, probablemente no sea porque quiera probarla; es solo parte del accesorio de prueba. En este caso, puede asumir con seguridad que funciona según lo previsto, por lo que inicializarlo en la declaración no será un problema.

Por lo que vale, trabajo en una base de código desarrollada por TDD razonablemente grande, de varios años de antigüedad. Inicializamos habitualmente cosas en sus declaraciones en el código de prueba, y en el año y medio que llevo en este proyecto, nunca ha causado un problema. Entonces hay al menos alguna evidencia anecdótica de que es algo razonable de hacer.