fbpx

JUnit 5 – PRUEBAS UNITARIAS en JAVA

¿Qué es JUnit?

JUnit es el framework de prueba de unitarias más populares en el ecosistema de Java. La versión JUnit 5 contiene una serie de innovaciones interesantes, con el objetivo de admitir nuevas funciones en Java 8 y superior, así como permitir muchos estilos diferentes de prueba.

Vídeo explicativo

JUnit 5 dependencias

Configurar JUnit 5.x.0 es bastante sencillo, solo necesitamos agregar la siguiente dependencia:

Maven

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>5.9.0</version>
    <scope>test</scope>
</dependency>

Gradle

Groovy:

testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.9.0'

Kotlin:

testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.0")

Arquitectura

A diferencia de las versiones anteriores de JUnit, JUnit 5 se compone de varios módulos diferentes de tres subproyectos diferentes.

JUnit Platform

La JUnit platform sirve como base para lanzar marcos de prueba en la JVM. También define la TestEngine API para desarrollar un marco de prueba que se ejecuta en la plataforma. Además, la plataforma proporciona un Lanzador de Consola para iniciar la plataforma desde la línea de comandos y el JUnit Platform Suite Engine para ejecutar un conjunto de pruebas personalizado utilizando uno o más motores de prueba en la plataforma. El soporte de primera clase para JUnit Platform también existe en los IDE populares (IntelliJ IDEA, Eclipse, NetBeans y Visual Studio Code) y herramientas de compilación (Gradle, Maven y Ant).

JUnit Jupiter

Este módulo incluye nuevos modelos de programación y extensión para escribir pruebas en JUnit 5. Las nuevas anotaciones en comparación con JUnit 4 son:

  • @TestFactory: denota un método que es una fábrica de pruebas para pruebas dinámicas
  • @DisplayName: define un nombre para mostrar personalizado para una clase de prueba o un método de prueba
  • @Nested: indica que la clase anotada es una clase de prueba anidada y no estática
  • @Tag: declara etiquetas para pruebas de filtrado
  • @ExtendWith: registra extensiones personalizadas
  • @BeforeEach: indica que el método anotado se ejecutará antes de cada método de prueba (anteriormente @Before)
  • @AfterEach: indica que el método anotado se ejecutará después de cada método de prueba (anteriormente @After)
  • @BeforeAll: indica que el método anotado se ejecutará antes que todos los métodos de prueba en la clase actual (anteriormente @BeforeClass)
  • @AfterAll: indica que el método anotado se ejecutará después de todos los métodos de prueba en la clase actual (anteriormente @AfterClass)
  • @Disable: desactiva una clase o método de prueba (anteriormente @Ignore)

JUnit Vintage

JUnit Vintage proporciona un TestEngine para ejecutar pruebas basadas en JUnit 3 y JUnit 4 en la plataforma. Requiere que JUnit 4.12 o posterior este añadido en el classpath o en la ruta del módulo (module path).

Ejemplo básico JUnit

El siguiente ejemplo proporciona un vistazo a los requisitos mínimos para escribir una prueba en JUnit Jupiter. Las siguientes secciones de este capítulo proporcionarán más detalles sobre todas las funciones disponibles.

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

import ejemplo.util.Calculadora;

import org.junit.jupiter.api.Test;

class MiPrimerJUnitJupiterTests {

    private final Calculadora calculadora= new Calculadora();

    @Test
    void addition() {
        assertEquals(2, calculadora.sumar(1, 1));
    }

}

Anotaciones

@BeforeAll y @BeforeEach

@BeforeEach

Indica que el método anotado debe ejecutarse antes de cada método @Test, @RepeatedTest, @ParameterizedTest o @TestFactory en la clase actual; análogo a @Before de JUnit 4. Dichos métodos se heredan, a menos que se anulen o reemplacen (es decir, se reemplacen en función de la firma únicamente, independientemente de las reglas de visibilidad de Java).

@BeforeEach
void init() {
    log.info("@BeforeEach - se ejecuta antes de cada método de prueba en esta clase");
}

@BeforeAll

Indica que el método anotado debe ejecutarse antes que todos los métodos @Test, @RepeatedTest, @ParameterizedTest y @TestFactory en la clase actual; análogo a @BeforeClass de JUnit 4. Dichos métodos son heredados, a menos que estén ocultos, anulados o reemplazados (es decir, reemplazados solo en función de la firma, independientemente de las reglas de visibilidad de Java), y deben ser estáticos.

@BeforeAll
static void setup() {
    log.info("@BeforeAll - se ejecuta una vez antes de todos los métodos de prueba en esta clase");
}

@DisplayName y @Disabled

@DisplayName

Declara un nombre para mostrar personalizado para la clase de prueba o el método de prueba. Estas anotaciones no se heredan.

@DisplayName("Single test successful")
@Test
void testSimpleTest() {
    log.info("Success");
}

@Disabled

Se utiliza para deshabilitar una clase de prueba o un método de prueba; análogo a @Ignore de JUnit 4. Estas anotaciones no se heredan.

@Test
@Disabled("Este test esta desactivado")
void testVacio() {
}

@AfterEach and @AfterAll

@AfterEach

Indica que el método anotado debe ejecutarse después de cada método @Test, @RepeatedTest, @ParameterizedTest o @TestFactory en la clase actual; análogo al @After de JUnit 4. Dichos métodos se heredan, a menos que se anulen o reemplacen (es decir, se reemplacen en función de la firma únicamente, independientemente de las reglas de visibilidad de Java).

@AfterEach
void tearDown() {
    log.info("@AfterEach - ejecutado después de cada método de prueba.");
}

@AfterAll

Indica que el método anotado debe ejecutarse después de todos los métodos @Test, @RepeatedTest, @ParameterizedTest y @TestFactory en la clase actual; análogo a @AfterClass de JUnit 4. Dichos métodos son heredados, a menos que estén ocultos, anulados o reemplazados (es decir, reemplazados solo en función de la firma, independientemente de las reglas de visibilidad de Java), y deben ser estáticos a menos que se utilice el ciclo de vida de la instancia de prueba “por clase”.

@AfterAll
static void despuesDeTodos() {
    log.info("@AfterAll -ejecutado después de todos los métodos de prueba.");
}

Assertions y Assumptions

Assertions

JUnit Jupiter viene con muchos de los métodos de aserción (assertions) que tiene JUnit 4 y agrega algunos que se prestan bien para usarse con Java 8 lambdas. Todas las afirmaciones de JUnit Jupiter son métodos estáticos en la clase org.junit.jupiter.api.Assertions.

@Test
void testConExpresionLambda() {
    assertTrue(5 > 4, () -> "5 es más grande que 4");
}

Aunque el ejemplo anterior es trivial, una ventaja de usar la expresión lambda para el mensaje de aserción es que se evalúa de forma perezosa (lazy), lo que puede ahorrar tiempo y recursos si la construcción del mensaje es costosa.

Ahora también es posible agrupar aserciones con assertAll(), que informará cualquier aserción fallida dentro del grupo con un MultipleFailuresError:

 @Test
 void gruppDeAssertions() {
     int[] nums = {0, 1, 2, 3, 4};
     assertAll("números",
         () -> assertEquals(nums[0], 1),
         () -> assertEquals(nums[3], 3),
         () -> assertEquals(nums[4], 1)
     );
 }

Assumptions

Las suposiciones (Assumptions) se utilizan para ejecutar pruebas solo si se cumplen ciertas condiciones. Esto generalmente se usa para condiciones externas que se requieren para que la prueba se ejecute correctamente, pero que no están directamente relacionadas con lo que se está probando.

Podemos declarar una suposición con assumeTrue(), assumeFalse(), and assumingThat():

@Test
void trueAssumption() {
    assumeTrue(5 > 1);
    assertEquals(5 + 2, 7);
}

@Test
void falseAssumption() {
    assumeFalse(5 < 1);
    assertEquals(5 + 2, 7);
}

@Test
void assumptionThat() {
    String cadena = "Un string";
    assumingThat(
        cadena.equals("Un string"),
        () -> assertEquals(2 + 2, 4)
    );
}

Si una suposición falla, se lanza una excepción TestAbortedException y la prueba simplemente se omite.

Testeando Excepciones

Hay dos formas de probar excepciones en JUnit 5, las cuales podemos implementar usando el método assertThrows():

@Test
void tirarExcepcionTest() {
    Throwable exception = assertThrows(UnsupportedOperationException.class, () -> {
      throw new UnsupportedOperationException("No soportada");
    });
    assertEquals("No soportada", exception.getMessage());
}

@Test
void controlarExcepcionTest() {
    String str = null;
    assertThrows(IllegalArgumentException.class, () -> {
      Integer.valueOf(str);
    });
}

Los comentarios están cerrados.