Junit Exception Handling

Junit: Exception Handling

JUnit Jupiter proporciona un sólido soporte para manejar excepciones en las pruebas. Esto incluye los mecanismos integrados para gestionar fallos de prueba debido a excepciones, el papel de las excepciones en la implementación de aserciones y suposiciones, y cómo específicamente afirmar condiciones que no generen excepciones en el código.

Uncaught Exceptions

En JUnit Jupiter, si una excepción es lanzada desde un método de prueba, un método de ciclo de vida o una extensión y no se captura dentro de ese método de prueba, método de ciclo de vida o extensión, el marco de trabajo marcará la prueba o la clase de prueba como fallida.

❕❕

Las assumptions fallidas se desvían de esta regla general.

A diferencia de las aserciones fallidas, las suposiciones fallidas no provocan un fallo en la prueba; en cambio, una suposición fallida provoca que la prueba sea abortada.

See Assumptions for further details and examples.


En el siguiente ejemplo, el método failsDueToUncaughtException() lanza una ArithmeticException. Dado que la excepción no se captura dentro del método de prueba, JUnit Jupiter marcará la prueba como fallida.

private final Calculator calculator = new Calculator();

@Test
void failsDueToUncaughtException() {
    // The following throws an ArithmeticException due to division by
    // zero, which causes a test failure.
    calculator.divide(1, 0);
}

❕❕

Es importante señalar que especificar una cláusula throws en el método de prueba no tiene efecto en el resultado de la prueba. JUnit Jupiter no interpreta una cláusula throws como una expectativa o aserción sobre qué excepciones debe lanzar el método de prueba. Una prueba solo falla si se lanza una excepción de manera inesperada o si una aserción falla.

Failed Assertions

Las aserciones en JUnit Jupiter se implementan mediante excepciones. El marco de trabajo proporciona un conjunto de métodos de aserción en la clase org.junit.jupiter.api.Assertions, los cuales lanzan un AssertionError cuando una aserción falla. Este mecanismo es un aspecto clave de cómo JUnit maneja los fallos de aserción como excepciones. Consulta la sección de Aserciones para obtener más información sobre el soporte de aserciones en JUnit Jupiter.

❕Las bibliotecas de aserción de terceros pueden optar por lanzar un AssertionError para señalar una aserción fallida; sin embargo, también pueden elegir lanzar diferentes tipos de excepciones para señalar fallos. Consulta también: Bibliotecas de Aserción de Terceros.

💡JUnit Jupiter en sí mismo no diferencia entre las aserciones fallidas (AssertionError) y otros tipos de excepciones. Todas las excepciones no capturadas provocan un fallo en la prueba. Sin embargo, los Entornos de Desarrollo Integrados (IDEs) y otras herramientas pueden distinguir entre estos dos tipos de fallos al verificar si la excepción lanzada es una instancia de AssertionError.

En el siguiente ejemplo, el método failsDueToUncaughtAssertionError() lanza un AssertionError. Dado que la excepción no se captura dentro del método de prueba, JUnit Jupiter marcará la prueba como fallida.

private final Calculator calculator = new Calculator();

@Test
void failsDueToUncaughtAssertionError() {
    // The following incorrect assertion will cause a test failure.
    // The expected value should be 2 instead of 99.
    assertEquals(99, calculator.add(1, 1));
}

Asserting Expected Exceptions

JUnit Jupiter ofrece aserciones especializadas para verificar que se lanzan excepciones específicas bajo condiciones esperadas. Las aserciones assertThrows() y assertThrowsExactly() son herramientas clave para validar que tu código responde correctamente a las condiciones de error al lanzar las excepciones apropiadas.

Using assertThrows()

El método assertThrows() se utiliza para verificar que se lance un tipo específico de excepción durante la ejecución de un bloque ejecutable proporcionado. No solo verifica el tipo de la excepción lanzada, sino también sus subclases, lo que lo hace adecuado para pruebas de manejo de excepciones más generalizadas. El método de aserción assertThrows() devuelve el objeto de la excepción lanzada, lo que permite realizar aserciones adicionales sobre la misma.

@Test
void testExpectedExceptionIsThrown() {
    // The following assertion succeeds because the code under assertion
    // throws the expected IllegalArgumentException.
    // The assertion also returns the thrown exception which can be used for
    // further assertions like asserting the exception message.
    IllegalArgumentException exception =
        assertThrows(IllegalArgumentException.class, () -> {
            throw new IllegalArgumentException("expected message");
        });
    assertEquals("expected message", exception.getMessage());

    // The following assertion also succeeds because the code under assertion
    // throws IllegalArgumentException which is a subclass of RuntimeException.
    assertThrows(RuntimeException.class, () -> {
        throw new IllegalArgumentException("expected message");
    });
}

Using assertThrowsExactly()

El método assertThrowsExactly() se utiliza cuando necesitas afirmar que la excepción lanzada es exactamente de un tipo específico, sin permitir subclases del tipo de excepción esperado. Esto es útil cuando se necesita validar un comportamiento preciso del manejo de excepciones. Similar a assertThrows(), el método de afirmación assertThrowsExactly() también devuelve el objeto de la excepción lanzada, lo que permite realizar aserciones adicionales sobre ella.

@Test
void testExpectedExceptionIsThrown() {
    // The following assertion succeeds because the code under assertion throws
    // IllegalArgumentException which is exactly equal to the expected type.
    // The assertion also returns the thrown exception which can be used for
    // further assertions like asserting the exception message.
    IllegalArgumentException exception =
        assertThrowsExactly(IllegalArgumentException.class, () -> {
            throw new IllegalArgumentException("expected message");
        });
    assertEquals("expected message", exception.getMessage());

    // The following assertion fails because the assertion expects exactly
    // RuntimeException to be thrown, not subclasses of RuntimeException.
    assertThrowsExactly(RuntimeException.class, () -> {
        throw new IllegalArgumentException("expected message");
    });
}

Asserting That no Exception is Expected

Aunque cualquier excepción lanzada desde un método de prueba hará que la prueba falle, existen ciertos casos en los que puede ser beneficioso afirmar explícitamente que no se lanza una excepción para un bloque de código determinado dentro de un método de prueba. La aserción assertDoesNotThrow() se puede usar cuando deseas verificar que una pieza específica de código no lanza ninguna excepción.

@Test
void testExceptionIsNotThrown() {
    assertDoesNotThrow(() -> {
        shouldNotThrowException();
    });
}

void shouldNotThrowException() {
}

❕Las bibliotecas de aserciones de terceros a menudo ofrecen soporte similar. Por ejemplo, AssertJ tiene assertThatNoException().isThrownBy(() → …). See also: Third-party Assertion Libraries.