Resumen¶
- Los drivers son fragmentos de código que implementamos para ejecutar los tests de manera automática.
- SUT = System Under Test, código a probar.
- Las pruebas unitarias se centran en unidades de programa: métodos java.
- En cada driver hay tres fases. Arrange, preparar los datos; Act, invocar la SUT; y Assert, comparar resultados.
- En JUnit los drivers son métodos sin parámetros, tipo void y anotado con
@Test. - JUnit tiene métodos como
assertEquals,assertTrue,assertThrows, para verificar los resultados. Todos se pueden agrupar en sentenciasassertAll. - Los tests parametrizados permiten ejecutar un mismo método de prueba con parámetros distintos. Puede utilizarse
@ValueSourceo@MethodSource. - El plugin maven-surefire-plugin gestiona la ejecución de los tests invocando a la librería
junit-jupiter. - Podemos incluir filtros en los comandos maven para excluir tests o ejecutar tests específicos.
- Los informes se guardan en
target/surefire-reportsen formatos de texto. Pueden presentarse como pass, failure o error. - El ciclo de vida por defecto de maven tiene tres fases: compile, test-compile y test.
Drivers¶
Automatización de las pruebas¶
Se trata de implementar código, drivers, para ejecutar los tests de forma automática.
El código de pruebas tendrá que:
1. Proporcionar datos de entrada + resultado esperado al código a probar (SUT)
2. Obtener el resultado real.
3. Comparar el resultado esperado con el real.
4. Emitir un informe que verifica que no haya bugs en el código.
SUT = System Under Test -> El código a probar.
Pruebas unitarias¶
- Las pruebas unitarias se centran en unidades de programa (en este caso, cada unidad se define como un método Java).
- El objetivo es aislar cada método y detectar posibles defectos.
- Creamos un test, por cada unidad de programa presente.
Pruebas de Unidad Dinámicas: Drivers¶
- Un driver es un código conductor para ejecutar un caso de prueba de forma automatizada.
- Cada driver se encarga de:
- Preparar los datos de entrada (Arrange).
- Invocar el método a probar (Act).
- Comparar resultado real vs. esperado (Assert).
- Emitir un informe.
- Así, cada caso de prueba tendrá un driver, y ese driver llamará a la SUT (System Under Test), que en pruebas unitarias se corresponde con un único método.
- Cada unidad es un método java.
Implementación de drivers con JUnit 5¶
JUnit 5¶
- JUnit 5 es una librería Java para implementar y ejecutar tests.
Cómo identificamos un driver (test) con JUnit 5?¶
- Cada driver se define como un método sin parámetros, con valor de retorno
voidy anotado con@Test. - La clase de prueba debe estar en el mismo paquete que la clase a probar y que su nombre sea igual añadiendo
Test,TrianguloTest.
class TrianguloTest {
@Test
void C1_testTipoEquilatero() {
// 1. Arrange
int a = 1, b = 1, c = 1;
String esperado = "Equilatero";
Triangulo tri = new Triangulo();
// 2. Act
String real = tri.tipo_triangulo(a,b,c);
// 3. Assert
assertEquals(esperado, real);
}
}
Patrón AAA (Arrange-Act-Assert)¶
- Arrange: Preparar datos de entrada, instanciar la SUT y definir precondiciones.
- Act: Invocar el método SUT con los datos preparados.
- Assert: Comprobar la coincidencia entre resultado real y esperado usando aserciones de JUnit (por ejemplo,
assertEquals).
Los nombres de los drivers seguirán el formato: ID_MethodName_ExpectedResult_When_Conditions.
Sentencias de aserción (Assert)¶
- JUnit provee métodos estáticos (
assertEquals,assertTrue,assertThrows) para verificar la validez de los resultados. - El orden de parámetros es
assertEquals(esperado, real). - Cuando un
assertfalla, lanza una excepción (AssertionFailedError), marcando el test como Failure. - Para varias comprobaciones en un mismo test, hay que usar
assertAll, que ejecuta todas las aserciones (si una falla, igualmente se registra el fallo, pero se reportan todas las discrepancias).
Agrupando las excepciones:
@Test
public void C3_add_should_not_add_element_when_dataArray_is_full() {
int[] arrayEsperado = {1,2,3,4,5,6,7,8,9,10};
int numElemEsperado = 10;
int[] arrayEstadoInicial = Arrays.copyOf(arrayEsperado, arrayEsperado.length);
DataArray coleccion = new DataArray(arrayEstadoInicial, 10);
coleccion.add(11);
//Agrupamos las aserciones. SE EJECUTAN TODAS siempre
assertAll("C3_dataArray no debe cambiar",
()-> assertArrayEquals(arrayEsperado, coleccion.getColeccion()),
()-> assertEquals(numElemEsperado, coleccion.size())
); //Se muestran todos los "fallos" producidos
}
No las agrupamos:
@Test
public void C3_add_should_not_add_element_when_dataArray_is_full() {
...
//No agrupamos las excepciones. Si falla la primera, la segunda NO se ejecuta
assertArrayEquals(arrayEsperado, coleccion.getColeccion());
assertEquals(numElemEsperado, coleccion.size());
}
Pruebas de excepciones¶
assertThrows¶
Incluye las excepciones heredadas de la excepción a capturar.
- Si se espera que la SUT lance una excepción, se usa
assertThrows(...). - Si se espera que no se lance ninguna excepción, se puede usar
assertDoesNotThrow(...).
En un assertThrows(...) si al ejecutar sut() no se lanza la excepción de tipo ExpectedException la ejecución del test fallará. Pasa lo mismo en assertDoesNotThrow(...) pero cuando no se lanza la excepción.
@Test
void excepcionLanzadaTest() {
ArithmeticException exception = assertThrows(
ArithmeticException.class, () -> sut.metodoDividir(10,0)
);
assertEquals("/ by zero", exception.getMessage());
}
assertThrowsExactly¶
No incluye las excepciones heredadas de la excepción a capturar.
Anotaciones @BeforeEach, @AfterEach, @BeforeAll, @AfterAll¶
- Permiten inicializar y finalizar el estado antes/después de cada test o de todos los tests, para reducir código repetido.
- Ejemplo:
@BeforeEachse ejecuta antes de cada test, por ejemplo para inicializar una variable común.@AfterEachjusto después de cada test, por ejemplo para cerrar ficheros o conexiones.@BeforeAlly@AfterAll(métodosstatic) se ejecutan una vez al inicio y final de la clase de prueba. Para acciones previas antes de TODOS los tests.
No debemos asumir nunca el orden de ejecución de los tests. No se deben implementar NUNCA tests cuya ejecución dependa del resultado de ejecutar otro test.

Tests parametrizados¶
- JUnit 5 permite la ejecución repetida de un mismo método de prueba con diferentes parámetros, usando anotaciones como
@ParameterizedTest,@ValueSource,@MethodSource, etc.
Con @ValueSource¶
@ParameterizedTest
@ValueSource(strings = {"racecar", "radar", "able was I ere I saw elba"})
void testPalindromes(String candidate) {
assertTrue(miClase.isPalindrome(candidate));
}
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running ppss.MiPruebaTest
-> testPalindromos(String) with argument [racecar] PASSED
-> testPalindromos(String) with argument [radar] PASSED
-> testPalindromos(String) with argument [able was I ere I saw elba] PASSED
Results:
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
-------------------------------------------------------
BUILD SUCCESS
-------------------------------------------------------
Con @MethodSource¶
Admite pasar múltiples parámetros simultáneamente.
@DisplayName("Calculadora_")
@ParameterizedTest(name = "[{index}]: se espera {0} para ({1}, {2})")
@MethodSource("casosDePrueba")
void testSuma(int resultadoEsperado, int a, int b) {
int resultadoReal = Calculadora.sumar(a, b); // SUT
Assertions.assertEquals(resultadoEsperado, resultadoReal);
}
// SIEMPRE privado, estático, sin parámetros y tipo Stream<Arguments>
private static Stream<Arguments> casosDePrueba() {
return Stream.of(
Arguments.of(5, 2, 3), // Queremos que funcione
Arguments.of(0, 0, 0), // Queremos que falle, imaginemos que la SUT retorna 1
Arguments.of(-1, -2, 1), // Queremos que falle
Arguments.of(7, 3, 4), // Queremos que funcione
Arguments.of(10, 9, 1) // Queremos que funcione
);
}
nameen@ParameterizedTestrepresenta una línea personalizada de salida la ejecución de cada test. Se muestra independientemente de si los tests pasan o fallan.@MethodSourceindica el métodoStream<Arguments>del que se obtienen los parámetros de los tests.- Los argumentos deben estar en el mismo orden que en el driver original.
- El método SIEMPRE debe ser privado, estático, no debe tener parámetros y debe ser de tipo
Stream<Arguments>.
- Opcionalmente se puede incluir el nombre personalizado para el test con
@DisplayName.
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running ppss.CalculadoraTest
+-- [OK] Calculadora_[1]: se espera 5 para (2, 3) - 0.001s
+-- [FAIL] Calculadora_[2]: se espera 0 para (0, 0) - 0.001s
org.opentest4j.AssertionFailedError:
expected: <0> but was: <1>
at org.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:55)
at org.junit.jupiter.api.Assertions.fail(Assertions.java:114)
at org.junit.jupiter.api.Assertions.assertEquals(Assertions.java:638)
...
+-- [FAIL] Calculadora_[3]: se espera -1 para (-2, 1) - 0.001s
org.opentest4j.AssertionFailedError:
expected: <-1> but was: <0>
...
+-- [OK] Calculadora_[4]: se espera 7 para (3, 4) - 0.001s
+-- [OK] Calculadora_[5]: se espera 10 para (9, 1) - 0.001s
Results:
Tests run: 5, Failures: 2, Errors: 0, Skipped: 0
-------------------------------------------------------
BUILD SUCCESS
-------------------------------------------------------
JUnit 5 y Maven¶
Dependencia en el pom.xml¶
Para compilar y ejecutar pruebas con JUnit 5, se añade la dependencia "junit-jupiter", lo que da acceso a las clases del paquete org.junit.jupiter.api y org.junit.jupiter.params.
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.11.4</version>
<scope>test</scope>
</dependency>
<scope>test</scope>significa que solo se usa en la fase de pruebas, sin incluirse en el runtime normal de la aplicación. Para ejecutar los tests.
No usamos la librería de forma explícita porque maven la invoca a través de la goal
surefire:test.
Plugin surefire¶
- maven-surefire-plugin gestiona la ejecución de los tests en la fase
test. - Se puede sobrescribir la versión por defecto en el
pom.xml:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.2</version>
</plugin>
- Su comando principal
mvn testbusca y ejecuta las clases cuyo nombre coincida con los patrones**/Test*.java,**/*Test.java,**/*TestCase.java, que estén anotados con@Testo@ParameterizedTest. - La goal por defecto de de
mvn testessurefire:test. - Tiene empaquetado
jarpor defecto. - Si un test falla, se produce
BUILD FAILURE.
Filtros¶
mvn test -Dgroups=rapidosejecuta solo los tests con el tag "rapidos".mvn test -Dgroups=etiqueta1,etiqueta2 -DexcludedGroups=excluidosejecuta los tests con los tags "etiqueta1" y "etiqueta2" y que no estén anotados con el tag "excluidos".
Informes¶
- Maven genera por defecto informes en
target/surefire-reportsen formatos txt y xml. - Los resultados pueden presentarse:
- Pass: el resultado real coincide con el esperado.
- Failure: las aserciones de JUnit fallan (AssertionFailedError).
- Error: excepciones ajenas a las aserciones (NullPointerException).
- Maven los muestra en el terminal tras la ejecución de la fase
test, pero los genera JUnit.
Maven y pruebas unitarias¶
El ciclo de vida por defecto incluye:
1. compile¶
- Compila código en
src/main/java. - Goal por defecto:
compiler:compile.
2. test-compile¶
- Compila código de prueba en
src/test/java. - Goal por defecto
compiler:testCompile.
3. test¶
- Ejecuta las clases de prueba (drivers) mediante la goal por defecto
surefire:test. - Si algún test falla, se detiene el proceso de construcción.
Siguiente -> S04 - Pruebas de Caja Negra