Migración de JUnit 4 a JUnit 5

1. Información general

En este artículo, veremos cómo podemos migrar de JUnit 4 a la última versión de JUnit 5, con una descripción general de las diferencias entre las dos versiones de la biblioteca.

Para conocer las pautas generales sobre el uso de JUnit 5, consulte nuestro artículo aquí.

2. Ventajas de JUnit 5

Comencemos con la versión anterior: JUnit 4 tiene algunas limitaciones claras:

  • Todo el marco estaba contenido en una única biblioteca jar. Es necesario importar toda la biblioteca incluso cuando solo se requiere una función en particular. En JUnit 5, obtenemos más granularidad y podemos importar solo lo necesario
  • Un corredor de pruebas solo puede ejecutar pruebas en JUnit 4 a la vez (por ejemplo, SpringJUnit4ClassRunner o Parameterized ). JUnit 5 permite que varios corredores trabajen simultáneamente
  • JUnit 4 nunca avanzó más allá de Java 7, perdiéndose muchas características de Java 8. JUnit 5 hace un buen uso de las características de Java 8

La idea detrás de JUnit 5 era reescribir completamente JUnit 4 para resolver la mayoría de estos inconvenientes.

3. Diferencias

JUnit 4 se dividió en módulos que componen JUnit 5:

  • Plataforma JUnit: este módulo abarca todos los marcos de extensión en los que podríamos estar interesados ​​en la ejecución de pruebas, el descubrimiento y los informes.
  • JUnit Vintage: este módulo permite la compatibilidad con versiones anteriores de JUnit 4 o incluso JUnit 3

3.1. Anotaciones

JUnit 5 viene con cambios importantes dentro de sus anotaciones. El más importante es que ya no podemos usar la anotación @Test para especificar expectativas.

El parámetro esperado en JUnit 4:

@Test(expected = Exception.class) public void shouldRaiseAnException() throws Exception { // ... }

Ahora, podemos usar un método assertThrows :

public void shouldRaiseAnException() throws Exception { Assertions.assertThrows(Exception.class, () -> { //... }); }

El atributo de tiempo de espera en JUnit 4:

@Test(timeout = 1) public void shouldFailBecauseTimeout() throws InterruptedException { Thread.sleep(10); }

Ahora, el método assertTimeout en JUnit 5:

@Test public void shouldFailBecauseTimeout() throws InterruptedException { Assertions.assertTimeout(Duration.ofMillis(1), () -> Thread.sleep(10)); }

Otras anotaciones que se cambiaron dentro de JUnit 5:

  • @Before se cambia el nombre de la anotación a @BeforeEach
  • @After se cambia el nombre de la anotación a @AfterEach
  • La anotación @BeforeClass cambia de nombre a @BeforeAll
  • La anotación @AfterClass cambia de nombre a @AfterAll
  • La anotación @Ignore se renombra a @Disabled

3.2. Afirmaciones

Ahora podemos escribir mensajes de aserción en una lambda en JUnit 5, lo que permite que la evaluación perezosa omita la construcción de mensajes complejos hasta que sea necesario:

@Test public void shouldFailBecauseTheNumbersAreNotEqual_lazyEvaluation() { Assertions.assertTrue( 2 == 3, () -> "Numbers " + 2 + " and " + 3 + " are not equal!"); }

También podemos agrupar afirmaciones en JUnit 5:

@Test public void shouldAssertAllTheGroup() { List list = Arrays.asList(1, 2, 4); Assertions.assertAll("List is not incremental", () -> Assertions.assertEquals(list.get(0).intValue(), 1), () -> Assertions.assertEquals(list.get(1).intValue(), 2), () -> Assertions.assertEquals(list.get(2).intValue(), 3)); }

3.3. Supuestos

La nueva clase Supuestos ahora está en org.junit.jupiter.api.Assumptions . JUnit 5 es totalmente compatible con los métodos de suposiciones existentes en JUnit 4 y también agrega un conjunto de nuevos métodos para permitir la ejecución de algunas aserciones solo en escenarios específicos:

@Test public void whenEnvironmentIsWeb_thenUrlsShouldStartWithHttp() { assumingThat("WEB".equals(System.getenv("ENV")), () -> { assertTrue("http".startsWith(address)); }); }

3.4. Etiquetado y filtrado

En JUnit 4, podríamos agrupar las pruebas usando la anotación @Category . Con JUnit 5, la anotación @Category se reemplaza con la anotación @Tag :

@Tag("annotations") @Tag("junit5") @RunWith(JUnitPlatform.class) public class AnnotationTestExampleTest { /*...*/ }

Podemos incluir / excluir etiquetas particulares usando el complemento maven-surefire-plugin :

   maven-surefire-plugin   junit5     

3.5. Nuevas anotaciones para ejecutar pruebas

El @RunWith se utilizó para integrar el contexto de prueba con otros marcos o para cambiar el flujo de ejecución general en los casos de prueba en JUnit 4.

Con JUnit 5, ahora podemos usar la anotación @ExtendWith para proporcionar una funcionalidad similar.

Como ejemplo, para usar las funciones de Spring en JUnit 4:

@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration( {"/app-config.xml", "/test-data-access-config.xml"}) public class SpringExtensionTest { /*...*/ }

Ahora, en JUnit 5 es una extensión simple:

@ExtendWith(SpringExtension.class) @ContextConfiguration( { "/app-config.xml", "/test-data-access-config.xml" }) public class SpringExtensionTest { /*...*/ } 

3.6. Nuevas anotaciones de reglas de prueba

En JUnit 4, las anotaciones @Rule y @ ClassRule se usaron para agregar funcionalidad especial a las pruebas.

En JUnit 5. podemos reproducir la misma lógica usando la anotación @ExtendWith .

For example, say we have a custom rule in JUnit 4 to write log traces before and after a test:

public class TraceUnitTestRule implements TestRule { @Override public Statement apply(Statement base, Description description) { return new Statement() { @Override public void evaluate() throws Throwable { // Before and after an evaluation tracing here ... } }; } }

And we implement it in a test suite:

@Rule public TraceUnitTestRule traceRuleTests = new TraceUnitTestRule(); 

In JUnit 5, we can write the same in a much more intuitive manner:

public class TraceUnitExtension implements AfterEachCallback, BeforeEachCallback { @Override public void beforeEach(TestExtensionContext context) throws Exception { // ... } @Override public void afterEach(TestExtensionContext context) throws Exception { // ... } }

Using JUnit 5's AfterEachCallback and BeforeEachCallback interfaces available in the package org.junit.jupiter.api.extension, we easily implement this rule in the test suite:

@RunWith(JUnitPlatform.class) @ExtendWith(TraceUnitExtension.class) public class RuleExampleTest { @Test public void whenTracingTests() { /*...*/ } }

3.7. JUnit 5 Vintage

JUnit Vintage aids in the migration of JUnit tests by running JUnit 3 or JUnit 4 tests within the JUnit 5 context.

We can use it by importing the JUnit Vintage Engine:

 org.junit.vintage junit-vintage-engine ${junit5.vintage.version} test 

4. Conclusion

Como hemos visto en este artículo, JUnit 5 es una versión modular y moderna del marco JUnit 4. Hemos introducido las principales diferencias entre estas dos versiones y hemos indicado cómo migrar de una a otra.

La implementación completa de este tutorial se puede encontrar en GitHub.