W3docs

Ciclo de vida de pruebas JUnit en Java

Ciclo de vida de instancias de prueba y comportamiento por método vs. por clase en JUnit 5.

Cada prueba de JUnit 5 se ejecuta dentro de un ciclo de vida bien definido: una secuencia de hooks de configuración y limpieza que se disparan alrededor de tus métodos @Test en un orden garantizado. Entender ese orden — y la regla de que JUnit crea una nueva instancia de la clase de prueba por cada método de prueba — es lo que distingue los conjuntos de pruebas frágiles y dependientes del orden de los limpios y aislados. Este capítulo recorre las cinco anotaciones del ciclo de vida y los dos ciclos de vida de instancia que ofrece JUnit.

Las cinco anotaciones del ciclo de vida

JUnit 5 (el paquete org.junit.jupiter.api) define cuatro anotaciones de callback que rodean tus pruebas, más el propio @Test. Para un recorrido más completo de cada una, consulta anotaciones JUnit; si eres nuevo en el framework, comienza con la introducción a JUnit.

AnotaciónSe ejecutaEl método debe ser
@BeforeAllUna vez, antes de cualquier prueba en la clasestatic (en el ciclo de vida por defecto)
@BeforeEachAntes de cada método @Testinstancia
@TestLa prueba en síinstancia
@AfterEachDespués de cada método @Testinstancia
@AfterAllUna vez, después de que todas las pruebas hayan finalizadostatic (en el ciclo de vida por defecto)

Una clase de prueba con tres pruebas dispara @BeforeAll una vez, luego @BeforeEach@Test@AfterEach tres veces, y luego @AfterAll una vez.

import org.junit.jupiter.api.*;

class CalculatorTest {
  @BeforeAll  static void initSuite() { System.out.println("once, up front"); }
  @BeforeEach void setUp()           { System.out.println("before each test"); }

  @Test void add()      { Assertions.assertEquals(4, 2 + 2); }
  @Test void subtract() { Assertions.assertEquals(0, 2 - 2); }

  @AfterEach void tearDown()    { System.out.println("after each test"); }
  @AfterAll  static void close() { System.out.println("once, at the end"); }
}

Una instancia nueva por método de prueba

La regla más importante del ciclo de vida: por defecto, JUnit construye una nueva instancia de la clase de prueba antes de cada método de prueba. Los campos que modificas en una prueba no pueden filtrarse a otra, porque la siguiente prueba se ejecuta sobre un objeto diferente. Esto es lo que hace que las pruebas sean independientes del orden de ejecución.

class IsolationTest {
  private int counter = 0; // re-initialised for every test

  @Test void first()  { counter++; Assertions.assertEquals(1, counter); }
  @Test void second() { counter++; Assertions.assertEquals(1, counter); } // also 1, not 2
}

Ambas pruebas ven counter == 1. Si JUnit reutilizara una instancia, la segunda prueba observaría 2 y pasaría o fallaría dependiendo del orden — exactamente la fragilidad que este diseño previene.

PER_METHOD vs. PER_CLASS

Puedes dejar de usar la instancia por método con @TestInstance(Lifecycle.PER_CLASS). Entonces JUnit crea una instancia para toda la clase, los campos de instancia persisten entre pruebas y — como conveniencia — @BeforeAll/@AfterAll pueden ser no static.

AspectoPER_METHOD (por defecto)PER_CLASS
Instancias creadasuna por @Testuna por clase
Estado del campo de instanciareiniciado en cada pruebacompartido entre pruebas
@BeforeAll/@AfterAlldebe ser staticpueden ser métodos de instancia
Mejor paramáximo aislamientoconfiguración compartida costosa
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.TestInstance.Lifecycle;

@TestInstance(Lifecycle.PER_CLASS)
class SharedFixtureTest {
  @BeforeAll void openConnection() { /* non-static is now legal */ }
  @AfterAll  void closeConnection() { }
}

Recurre a PER_CLASS solo cuando la configuración sea genuinamente costosa y segura de compartir. El valor por defecto te proporciona aislamiento de forma gratuita.

Las aserciones son la forma en que una prueba reporta un fallo

Un ciclo de vida existe para ejecutar aserciones. Assertions.assertEquals(expected, actual) lanza un AssertionFailedError cuando los valores difieren, lo que aborta esa prueba individual (su @AfterEach aún se ejecuta) y la marca como fallida — las otras pruebas continúan. Consulta aserciones JUnit para el conjunto completo de métodos assert*.

import static org.junit.jupiter.api.Assertions.*;

@Test void example() {
  assertEquals(42, compute());
  assertTrue(isReady());
  assertThrows(IllegalArgumentException.class, () -> parse("bad"));
}

Un ejemplo detallado: trazando el ciclo de vida manualmente

No hay un ejecutor JUnit en este playground de código, por lo que el programa siguiente modela el ciclo de vida en código JDK puro: dispara los hooks en el orden de JUnit, contrasta PER_METHOD (una nueva instancia por prueba) con PER_CLASS (una instancia compartida), y termina con un pequeño arnés de auto-verificación al estilo de assertEquals.

java— editable, runs on the server

Lo que se puede concluir de la ejecución:

  • El bloque PER_METHOD imprime instance#1, instance#2, instance#3 para las tres pruebas, demostrando la regla por defecto de JUnit: se construye una nueva instancia de prueba para cada método @Test, por lo que ninguna prueba puede ver el estado modificado de otra.
  • En PER_METHOD, cada línea [TEST] reporta counter=1, nunca 2 ni 3. Cada instancia obtuvo su propio campo recién inicializado, razón por la que las pruebas se mantienen independientes del orden de ejecución — el beneficio principal del ciclo de vida por defecto.
  • El bloque PER_CLASS reutiliza instance#1 para las tres pruebas, y su counter sube 1 → 2 → 3. Con una instancia compartida, el estado del campo de instancia se filtra deliberadamente entre pruebas — útil para fixtures compartidos costosos, peligroso si lo olvidas.
  • @BeforeAll y @AfterAll aparecen exactamente una vez por bloque, envolviendo los pares @BeforeEach/@AfterEach por prueba que se disparan tres veces — el orden exacto de anidamiento que JUnit garantiza alrededor de tus pruebas.
  • El arnés final imprime PASS: para las tres verificaciones; un check fallido lanza un AssertionError con un mensaje FAIL:, reflejando cómo Assertions.assertEquals aborta una sola prueba con un AssertionFailedError mientras deja que las demás continúen.

Capítulos relacionados

Práctica

Práctica
En JUnit 5 con el ciclo de vida de instancia de prueba por defecto, ¿cuántas instancias de una clase de prueba con tres métodos @Test se crean cuando se ejecuta la clase?
En JUnit 5 con el ciclo de vida de instancia de prueba por defecto, ¿cuántas instancias de una clase de prueba con tres métodos @Test se crean cuando se ejecuta la clase?
Was this page helpful?