¿Cómo probar que no se lanza ninguna excepción?

Resuelto Ankit Dhingra asked hace 11 años • 20 respuestas

Sé que una forma de hacerlo sería:

@Test
public void foo() {
   try {
      // execute code that you expect not to throw Exceptions.
   } catch(Exception e) {
      fail("Should not have thrown any exception");
   }
}

¿Existe alguna forma más limpia de hacer esto? (¿Probablemente usando Junit's @Rule?)

Ankit Dhingra avatar Jul 19 '13 01:07 Ankit Dhingra
Aceptado

JUnit 5 (Jupiter) proporciona tres funciones para verificar la ausencia/presencia de excepciones:

assertAll​()

Afirma que todos los proporcionados executables
  no arrojan excepciones.

assertDoesNotThrow​()

Afirma que la ejecución del /
  proporcionado no arroja ningún tipo de excepción .executablesupplier

  Esta función está disponible
  desde JUnit 5.2.0 (29 de abril de 2018).

assertThrows​()

Afirma que la ejecución de lo proporcionado executable
arroja una excepción expectedType
  y devuelve la excepción .

Ejemplo

package test.mycompany.myapp.mymodule;

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

import org.junit.jupiter.api.Test;

class MyClassTest {

    @Test
    void when_string_has_been_constructed_then_myFunction_does_not_throw() {
        String myString = "this string has been constructed";
        assertAll(() -> MyClass.myFunction(myString));
    }
    
    @Test
    void when_string_has_been_constructed_then_myFunction_does_not_throw__junit_v520() {
        String myString = "this string has been constructed";
        assertDoesNotThrow(() -> MyClass.myFunction(myString));
    }

    @Test
    void when_string_is_null_then_myFunction_throws_IllegalArgumentException() {
        String myString = null;
        assertThrows(
            IllegalArgumentException.class,
            () -> MyClass.myFunction(myString));
    }

}
oHo avatar Aug 09 '2018 13:08 oHo

Estás abordando esto de manera incorrecta. Simplemente pruebe su funcionalidad: si se produce una excepción, la prueba fallará automáticamente. Si no se lanza ninguna excepción, todas sus pruebas aparecerán en verde.

He notado que esta pregunta genera interés de vez en cuando, así que la ampliaré un poco.

Antecedentes de las pruebas unitarias

Cuando realiza pruebas unitarias, es importante definir qué considera una unidad de trabajo. Básicamente: una extracción de su código base que puede incluir o no múltiples métodos o clases que representan una sola funcionalidad.

O, como se define en El arte de las pruebas unitarias, segunda edición de Roy Osherove , página 11:

Una prueba unitaria es un fragmento de código automatizado que invoca la unidad de trabajo que se está probando y luego verifica algunas suposiciones sobre un único resultado final de esa unidad. Una prueba unitaria casi siempre se escribe utilizando un marco de prueba unitaria. Se puede escribir fácilmente y se ejecuta rápidamente. Es confiable, legible y mantenible. Es consistente en sus resultados siempre que el código de producción no haya cambiado.

Lo que es importante tener en cuenta es que una unidad de trabajo generalmente no es solo un método, sino que en el nivel más básico es un método y luego se encapsula en otra unidad de trabajo.

ingrese la descripción de la imagen aquí

Lo ideal sería tener un método de prueba para cada unidad de trabajo separada, de modo que siempre pueda ver inmediatamente dónde van mal las cosas. En este ejemplo hay un método básico llamado getUserById()que devolverá un usuario y hay un total de 3 unidades de trabajo.

La primera unidad de trabajo debe probar si se devuelve o no un usuario válido en el caso de una entrada válida e inválida.
Cualquier excepción que genere la fuente de datos debe manejarse aquí: si no hay ningún usuario presente, debe haber una prueba que demuestre que se genera una excepción cuando no se puede encontrar al usuario. Una muestra de esto podría ser el IllegalArgumentExceptionque se pilla con la @Test(expected = IllegalArgumentException.class)anotación.

Una vez que haya manejado todos los casos de uso para esta unidad de trabajo básica, ascenderá un nivel. Aquí haces exactamente lo mismo, pero solo manejas las excepciones que provienen del nivel justo debajo del actual. Esto mantiene su código de prueba bien estructurado y le permite ejecutar rápidamente la arquitectura para encontrar dónde van mal las cosas, en lugar de tener que saltar por todos lados.

Manejo de la entrada válida y defectuosa de una prueba

En este punto debería quedar claro cómo vamos a manejar estas excepciones. Hay 2 tipos de entrada: entrada válida y entrada defectuosa (la entrada es válida en sentido estricto, pero no es correcta).

Cuando trabaja con entradas válidas , establece la expectativa implícita de que cualquier prueba que escriba funcionará.

Una llamada a un método de este tipo puede tener este aspecto: existingUserById_ShouldReturn_UserObject. Si este método falla (por ejemplo: se genera una excepción), entonces sabrá que algo salió mal y puede comenzar a investigar.

Al agregar otra prueba ( nonExistingUserById_ShouldThrow_IllegalArgumentException) que utiliza la entrada defectuosa y espera una excepción, puede ver si su método hace lo que se supone que debe hacer con una entrada incorrecta.

TL;DR

Estaba intentando hacer dos cosas en su prueba: verificar si hay entradas válidas y defectuosas. Al dividir esto en dos métodos, cada uno de los cuales hace una cosa, tendrá pruebas mucho más claras y una visión general mucho mejor de dónde salen mal las cosas.

Al tener en cuenta la unidad de trabajo por capas, también puede reducir la cantidad de pruebas que necesita para una capa que está más arriba en la jerarquía porque no tiene que tener en cuenta todo lo que podría haber salido mal en las capas inferiores: Las capas debajo de la actual son una garantía virtual de que sus dependencias funcionan y si algo sale mal, está en su capa actual (suponiendo que las capas inferiores no arrojen ningún error).

Jeroen Vannevel avatar Jul 18 '2013 18:07 Jeroen Vannevel