Aserciones JUnit en Java
Verifica el comportamiento en JUnit 5 con assertEquals, assertTrue, assertThrows, assertAll y más.
Una aserción es el momento en que una prueba deja de describir y comienza a verificar — establece lo que el código debería producir y falla la prueba si la realidad no coincide. JUnit 5 (la API Jupiter) reúne cada aserción como métodos static en una única clase, org.junit.jupiter.api.Assertions. Aprende ese puñado de métodos y podrás expresar casi cualquier expectativa: igualdad, verdad, nulidad, identidad, excepciones lanzadas, tiempos de espera y comprobaciones agrupadas. Este capítulo recorre los que usarás a diario.
Si eres nuevo en el framework, lee primero la introducción a JUnit y luego las anotaciones de JUnit para entender de dónde vienen los métodos @Test. Las aserciones viven dentro de esos métodos.
El modelo mental: una aserción fallida falla la prueba
Un método de prueba JUnit se ejecuta de arriba a abajo. Cada aserción que se cumple es silenciosa; la primera que no se cumple lanza un AssertionFailedError, que JUnit captura y registra como un fallo — el método se detiene ahí. Por lo tanto, las aserciones son los puntos de salida de la prueba. El orden convencional de argumentos es esperado primero, real segundo, y cada método acepta un mensaje final opcional que solo se usa cuando la comprobación falla:
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
class CalculatorTest {
@Test
void addsTwoNumbers() {
int result = 2 + 3;
assertEquals(5, result, "2 + 3 should equal 5");
}
}Se importan los métodos de forma estática (import static ... Assertions.*) para que el cuerpo de la prueba se lea como inglés llano — assertEquals(...), no Assertions.assertEquals(...).
Igualdad, verdad y nulidad
Estos cuatro cubren la gran mayoría de las aserciones reales:
| Método | Se cumple cuando |
|---|---|
assertEquals(expected, actual) | expected.equals(actual) es verdadero |
assertNotEquals(unexpected, actual) | los dos no son iguales |
assertTrue(condition) / assertFalse(condition) | el boolean es true / false |
assertNull(obj) / assertNotNull(obj) | la referencia es / no es null |
assertSame(expected, actual) | ambos apuntan al mismo objeto (==) |
assertArrayEquals(expected, actual) | los arrays son iguales elemento por elemento |
assertEquals("HELLO", "hello".toUpperCase());
assertTrue(List.of(1, 2, 3).contains(2), "list should contain 2");
assertNull(map.get("missing"));
assertNotSame(new String("a"), new String("a")); // distinct objects
assertArrayEquals(new int[]{1, 2, 3}, computeRange(3));assertEquals usa .equals(), por lo que dos objetos String distintos con los mismos caracteres pasan; assertSame usa ==, por lo que fallan. Recurre a assertSame solo cuando la identidad del objeto es lo que pretendes probar.
Probar excepciones con assertThrows
Una prueba a veces verifica que el código falla — que una entrada incorrecta lanza una excepción. assertThrows recibe el tipo de excepción y una lambda; se cumple solo si al ejecutar la lambda se lanza ese tipo (o un subtipo), y devuelve la excepción capturada para que puedas inspeccionar su mensaje:
@Test
void rejectsNullName() {
IllegalArgumentException ex = assertThrows(
IllegalArgumentException.class,
() -> greet(null));
assertEquals("name must not be null", ex.getMessage());
}Su imagen especular es assertDoesNotThrow, que falla si la lambda lanza cualquier cosa — útil para verificar que una ruta anteriormente defectuosa ahora se ejecuta sin problemas. (Para las reglas sobre las excepciones que lanza tu código, consulta excepciones en Java.)
Agrupación con assertAll
Por defecto, la primera aserción fallida termina el método, ocultando cualquier problema posterior. assertAll ejecuta cada aserción contenida incluso cuando algunas fallan, y luego reporta todos los fallos juntos — ideal para comprobar varias propiedades de un objeto:
@Test
void buildsCompleteUser() {
User u = User.of("[email protected]");
assertAll("user",
() -> assertEquals("[email protected]", u.email()),
() -> assertTrue(u.isActive()),
() -> assertNotNull(u.createdAt()));
}Si tanto el correo electrónico como el indicador activo están mal, una sola ejecución te informa de ambos — en lugar de corregir uno, volver a ejecutar y descubrir el siguiente.
Un ejemplo práctico: una ejecución de prueba autoverificable, sin framework
El ejecutor de código no tiene JUnit en su classpath, por lo que este programa implementa la misma idea en Java puro: pequeños helpers assertEquals, assertTrue, assertThrows y assertAll que se comportan como los de Jupiter — silenciosos en caso de éxito, ruidosos en caso de fallo — ejecutando un puñado de métodos bajo prueba e imprimiendo un recuento al estilo de un ejecutor al final. La API que realmente escribirías está en los bloques estáticos anteriores; esto muestra lo que hacen esos métodos.
Qué extraer de la ejecución:
- Las comprobaciones que se cumplen no producen ninguna línea de fallo en absoluto — solo se imprimen
add(2,3) verified == 5ytag conditions verified, reflejando la regla de JUnit de que una aserción satisfecha es silenciosa y solo los fallos hablan. assertThrowsimprimiócaught expected IllegalArgumentException: name must not be null, mostrando el patrón: la lambda debe lanzar, el tipo debe coincidir, y el mensaje de la excepción capturada está disponible para que lo verifiques a continuación.assertAll ran 3 checks, 0 failedconfirma el comportamiento de agrupación — las tres lambdas se ejecutaron y se contabilizaron juntas, que es exactamente por quéassertAllexpone cada problema en una sola pasada en lugar de detenerse en el primero.- El
SUMMARY -> passed: 7, failed: 0final cuenta siete aserciones individuales en todo el programa (una de igualdad, dos booleanas, una de lanzamiento y tres dentro deassertAll), la misma contabilidad que reporta un ejecutor real — cada llamada aassertXes una verificación. - Nada aquí importó un framework de pruebas, y sin embargo la estructura es idéntica a Jupiter: helpers que son silenciosos en caso de éxito y explícitos en caso de fallo. Sustituir estos por
org.junit.jupiter.api.Assertions.*cambiaría las importaciones, no la forma en que razonas sobre cada comprobación.
El orden de los argumentos confunde a casi todo el mundo al principio: es assertEquals(expected, actual), nunca al revés. Si los intercambias, la prueba sigue funcionando, pero un mensaje de fallo se lee al revés — afirma que tu valor correcto es incorrecto y el defectuoso es el correcto. Mantén el valor literal o conocido como bueno primero.
Qué ver a continuación
Las aserciones son solo una pieza de un método de prueba. Para ponerlas a trabajar de forma efectiva:
- Agrupa la configuración y el desmontaje a su alrededor con los callbacks del ciclo de vida de JUnit (
@BeforeEach,@AfterEach). - Ejecuta las mismas aserciones con muchas entradas sin copiar y pegar usando pruebas parametrizadas.
- Revisa la referencia de anotaciones para
@Test,@DisplayNamey@Disabled.