JUnit
- java
- Testing
-
[🔍🛠️ Dominando los Test Unitarios en JAVA JUnit 🚀💻 - YouTube](https://youtu.be/mEzoe6KSUu8) - JUnit 5 User Guide
🧩 Fundamentos de JUnit
JUnit es el framework estándar de testing para Java, utilizado para escribir y ejecutar pruebas unitarias automatizadas.
Su principal objetivo es validar el comportamiento esperado de métodos y clases, asegurando que los cambios no rompan funcionalidades existentes.
Características clave
- Compatibilidad con Maven y Gradle
- Anotaciones para definir el ciclo de vida del test (
@BeforeAll,@AfterEach, etc.) - Soporte para Testing parametrizado
- Integración con Mockito y AssertJ
- Extensiones con JUnit 5 Jupiter API
⚙️ Estructura de un Test
Un test en JUnit sigue una estructura simple:
- Preparación (Arrange): configurar los datos o dependencias necesarias.
- Ejecución (Act): ejecutar el método a probar.
- Verificación (Assert): comprobar que los resultados sean los esperados.
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class CalculadoraTest {
@Test
void suma_debeRetornarResultadoCorrecto() {
Calculadora calc = new Calculadora();
int resultado = calc.sumar(2, 3);
assertEquals(5, resultado);
}
}
`
🧱 Ciclo de vida de los Tests
JUnit permite controlar la inicialización y limpieza de recursos antes y después de los tests.
@BeforeAll // Ejecutado una vez antes de todos los tests
@BeforeEach // Ejecutado antes de cada test
@AfterEach // Ejecutado después de cada test
@AfterAll // Ejecutado una vez después de todos los tests
Ejemplo:
@BeforeEach
void configurar() {
// Configura datos o mocks antes de cada prueba
}
🧪 Tests Parametrizados
Permiten ejecutar el mismo test con múltiples valores de entrada.
@ParameterizedTest
@ValueSource(strings = {"madre", "padre", "hermano"})
void testLongitudCadenas(String palabra) {
assertTrue(palabra.length() > 3);
}
Tipos de fuentes de parámetros
@ValueSource— valores simples (int, String, etc.)@CsvSource— combina varios valores@MethodSource— obtiene datos desde un método@EnumSource— usa enums como datos de entrada
🧩 Assertions más usadas
assertEquals(expected, actual)assertTrue(condition)assertFalse(condition)assertThrows(Exception.class, () -> { ... })assertAll("grupo de asserts", () -> {...}, () -> {...})
assertAll("Validaciones múltiples",
() -> assertEquals(4, calc.sumar(2, 2)),
() -> assertThrows(ArithmeticException.class, () -> calc.dividir(10, 0))
);
🧰 Integración con Mockito
JUnit se complementa con Mockito para simular dependencias.
@ExtendWith(MockitoExtension.class)
class ServicioUsuarioTest {
@Mock
private RepositorioUsuario repo;
@InjectMocks
private ServicioUsuario servicio;
@Test
void debeGuardarUsuarioCorrectamente() {
when(repo.save(any())).thenReturn(new Usuario("Ana"));
Usuario u = servicio.crearUsuario("Ana");
assertEquals("Ana", u.getNombre());
}
}
🔄 Buenas prácticas
- Nombrar los tests de forma descriptiva (en inglés o español).
- Aislar cada prueba del entorno externo.
- Evitar dependencias entre tests.
- Usar CI/CD para ejecutar los tests automáticamente.
- Medir la Cobertura de código con JaCoCo o SonarQube.
- Organizar los tests por módulo o paquete.
⚡ Extensiones y complementos
@Tag: permite clasificar tests por categoría.@Disabled: desactiva temporalmente un test.@RepeatedTest: ejecuta un test varias veces.@DisplayName: define un nombre legible para el test.
@DisplayName("Prueba repetida de conexión")
@RepeatedTest(3)
void testConexion() {
assertTrue(servicio.conectar());
}
🧭 Integración con CI/CD
JUnit se ejecuta fácilmente en pipelines de GitHub Actions, Jenkins, o GitLab CI.
Los reportes XML generados pueden ser interpretados por herramientas de análisis continuo o dashboards personalizados.
📚 Recursos adicionales
- Testing de APIs REST con JUnit y Spring Boot
- Mocking avanzado con Mockito y JUnit
- Patrones de testing en Java
- Testing de integración con Testcontainers
JUnit - Conceptos Avanzados y Patrones de Testing
🧩 Testing de Integración con JUnit y Spring Boot
Los tests de integración validan la interacción entre varios componentes (servicios, controladores, repositorios, bases de datos).
Configuración básica
@SpringBootTest
@AutoConfigureMockMvc
class ControladorUsuarioIT {
@Autowired
private MockMvc mockMvc;
@Test
void debeRetornarListaUsuarios() throws Exception {
mockMvc.perform(get("/usuarios"))
.andExpect(status().isOk())
.andExpect(jsonPath("$").isArray());
}
}
`
Puntos clave
- Usa un contexto real de Spring con dependencias cargadas.
- Permite pruebas HTTP simuladas con MockMvc.
- Integra fácilmente bases de datos en memoria como H2.
🧱 Testing de Bases de Datos con Testcontainers
Permite ejecutar bases de datos reales (PostgreSQL, MySQL, MongoDB) en contenedores Docker durante los tests.
@Testcontainers
@SpringBootTest
class RepositorioUsuarioIT {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16")
.withDatabaseName("testdb")
.withUsername("user")
.withPassword("pass");
@Autowired
private RepositorioUsuario repo;
@Test
void debeGuardarYRecuperarUsuario() {
Usuario u = new Usuario("Carlos");
repo.save(u);
assertEquals("Carlos", repo.findById(u.getId()).get().getNombre());
}
}
Ventajas:
- Pruebas realistas, sin mocks.
- Reproduce entornos productivos.
- Limpieza automática al finalizar los tests.
🧪 Testing de APIs REST con JUnit + RestAssured
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
@Test
void debeRetornarUsuarios() {
given()
.when().get("/usuarios")
.then()
.statusCode(200)
.body("size()", greaterThan(0));
}
- Ideal para testing E2E de endpoints reales.
- Permite validar cabeceras, códigos HTTP y payloads JSON.
⚙️ Testeo de Eventos, Jobs y Colas
JUnit también puede validar comportamientos asíncronos o basados en colas como Kafka, RabbitMQ o Spring Events.
@ExtendWith(MockitoExtension.class)
class EventoUsuarioTest {
@Mock
private ApplicationEventPublisher publisher;
@InjectMocks
private ServicioUsuario servicio;
@Test
void debePublicarEventoAlCrearUsuario() {
servicio.crearUsuario("Ana");
verify(publisher).publishEvent(any(UsuarioCreadoEvent.class));
}
}
Puntos clave:
- Validar la emisión de eventos.
- Usar
awaitilitypara esperar eventos asincrónicos. - Testear consumidores o handlers específicos.
🧠 Patrones de Testing en JUnit
1. Given-When-Then
Facilita la lectura de tests como escenarios de comportamiento.
// Given
Usuario u = new Usuario("Pedro");
// When
u.activar();
// Then
assertTrue(u.isActivo());
2. Object Mother
Centraliza la creación de objetos de prueba.
class UsuarioMother {
static Usuario activo() {
Usuario u = new Usuario("Activo");
u.activar();
return u;
}
}
3. Fixture
Inicializa datos comunes a varios tests usando @BeforeEach.
4. Parameterized Behavior
Reduce duplicación en tests similares usando @MethodSource.
🧩 Extensiones de JUnit 5
JUnit 5 permite crear extensiones personalizadas (similar a middlewares).
public class TiempoDeEjecucionExtension implements BeforeTestExecutionCallback, AfterTestExecutionCallback {
public void beforeTestExecution(ExtensionContext ctx) { start = System.currentTimeMillis(); }
public void afterTestExecution(ExtensionContext ctx) {
System.out.println(ctx.getDisplayName() + " ejecutado en " + (System.currentTimeMillis() - start) + " ms");
}
}
Aplicación:
@ExtendWith(TiempoDeEjecucionExtension.class)
class MiTest {}
Permite:
- Medir tiempos.
- Inyectar dependencias personalizadas.
- Aplicar configuraciones globales.
🧭 Testing de Rendimiento y Carga Ligera
Para microbenchmarks o validación de rendimiento básico:
@Test
void debeEjecutarseEnMenosDe200ms() {
long start = System.currentTimeMillis();
servicio.procesarDatos();
assertTrue(System.currentTimeMillis() - start < 200);
}
También se puede integrar con:
- JMH (Java Microbenchmark Harness)
- Gatling para escenarios de carga HTTP
- JUnit Performance Plugin
🔄 Pruebas Condicionales y Dinámicas
JUnit permite ejecutar tests según condiciones del entorno o generar tests dinámicos en tiempo de ejecución.
@EnabledOnOs(OS.WINDOWS)
@DisabledIfEnvironmentVariable(named = "CI", matches = "true")
@Test
void soloEnLocalWindows() { ... }
@TestFactory
Stream<DynamicTest> testGenerado() {
return Stream.of("a", "b", "c")
.map(letra -> dynamicTest("Prueba con " + letra,
() -> assertTrue(letra.matches("[a-c]"))));
}
🧰 Integración con Arquitectura Limpia y DDD Domain-Driven Design
Los tests deben reflejar la arquitectura del dominio:
- Tests de dominio puro sin dependencias externas.
- Tests de infraestructura con adaptadores reales o mocks.
- Tests de aplicación verificando flujos completos.
Organización sugerida:
src/test/java
│
├── dominio/
├── aplicacion/
└── infraestructura/
📚 Recursos complementarios
- Mockito avanzado y testeo de excepciones en JUnit
- Testing de integración con Docker y Spring Boot
- JUnit 5 Extensions API
- Patrones de testing en arquitectura hexagonal
- Testing continuo con CI/CD y reportes JUnit XML
- Performance Testing con JMH y JUnit
🧪 JUnit - Casos Reales de Testing en Proyectos Java
- JUnit
- Mockito
- Spring Boot
- Testcontainers
- Testing de APIs
- Arquitectura Hexagonal
🧩 Caso 1: Testing de un Servicio con Dependencias
Prueba típica de un servicio de dominio que depende de un repositorio o cliente externo.
Usamos @Mock y @InjectMocks para aislar la lógica.
@ExtendWith(MockitoExtension.class)
class ServicioPedidoTest {
@Mock
private RepositorioPedido repo;
@InjectMocks
private ServicioPedido servicio;
@Test
void debeCrearPedidoYGuardarEnRepositorio() {
Pedido pedido = new Pedido("producto-123", 2);
when(repo.save(any())).thenReturn(pedido);
Pedido resultado = servicio.crearPedido("producto-123", 2);
assertEquals("producto-123", resultado.getProductoId());
verify(repo, times(1)).save(any(Pedido.class));
}
}
`
Puntos clave
- Se valida el flujo sin acceder a la base de datos.
verify()asegura que la interacción ocurrió como se esperaba.- Se controla completamente el entorno del test.
⚙️ Caso 2: Testing de Controladores REST con Spring Boot y MockMvc
Simula peticiones HTTP sin necesidad de servidor real.
@SpringBootTest
@AutoConfigureMockMvc
class ControladorProductoTest {
@Autowired
private MockMvc mockMvc;
@Test
void debeRetornarListaDeProductos() throws Exception {
mockMvc.perform(get("/productos"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.length()").value(greaterThan(0)));
}
}
Ventajas
- Evita levantar un servidor real.
- Permite validar rutas, payloads y respuestas HTTP.
- Integra con Jackson para serialización/deserialización JSON.
🧱 Caso 3: Testing con Testcontainers + PostgreSQL
Ejemplo de prueba de integración contra una base de datos real.
@Testcontainers
@SpringBootTest
class RepositorioProductoIT {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16")
.withDatabaseName("productos_test")
.withUsername("test")
.withPassword("test");
@Autowired
private RepositorioProducto repo;
@Test
void debeGuardarYRecuperarProducto() {
Producto p = new Producto("Taza cerámica", 12.5);
repo.save(p);
Optional<Producto> recuperado = repo.findByNombre("Taza cerámica");
assertTrue(recuperado.isPresent());
assertEquals(12.5, recuperado.get().getPrecio());
}
}
Beneficios
- Crea un entorno reproducible con datos reales.
- No requiere configuración manual de base de datos.
- Limpieza automática tras la ejecución.
📦 Caso 4: Testing de Publicación de Eventos
Simula el comportamiento de eventos de dominio o aplicación al ejecutarse una acción.
@ExtendWith(MockitoExtension.class)
class ServicioUsuarioTest {
@Mock
private ApplicationEventPublisher publisher;
@InjectMocks
private ServicioUsuario servicio;
@Test
void debePublicarEventoAlRegistrarUsuario() {
servicio.registrarUsuario("Juan", "correo@ejemplo.com");
verify(publisher).publishEvent(any(UsuarioRegistradoEvent.class));
}
}
Notas
- Ideal para sistemas basados en Event Driven Architecture.
- Se testean los efectos colaterales del dominio sin ejecutar el listener real.
🧠 Caso 5: Testing de Validaciones y Excepciones
Verifica que el dominio arroje errores cuando se incumplen reglas.
@Test
void debeLanzarExcepcionSiElPrecioEsNegativo() {
IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> {
new Producto("Camisa", -10);
});
assertEquals("El precio no puede ser negativo", ex.getMessage());
}
Recomendaciones
- Testear cada regla de negocio por separado.
- Usar
assertThrowspara validar flujos de error. - Mantener mensajes de error consistentes.
🧩 Caso 6: Testing Parametrizado de Reglas de Negocio
Reduce la duplicación de tests al probar varias combinaciones.
@ParameterizedTest
@CsvSource({
"100, true",
"0, false",
"-1, false"
})
void debeValidarMontoMinimo(double monto, boolean esperado) {
assertEquals(esperado, ServicioPago.esMontoValido(monto));
}
Beneficios
- Mayor cobertura con menos código.
- Se documentan las entradas y salidas esperadas.
- Facilita testear funciones puras o estáticas.
🌐 Caso 7: Testing de APIs Externas con WireMock
Simula respuestas HTTP de un servicio externo (por ejemplo, una API de pagos o clima).
@ExtendWith(WireMockExtension.class)
@WireMockTest(httpPort = 8081)
class ClienteApiClimaTest {
private ClienteApiClima cliente = new ClienteApiClima("http://localhost:8081");
@Test
void debeRetornarTemperaturaSimulada() {
stubFor(get(urlEqualTo("/clima?ciudad=Madrid"))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody("{\"temperatura\":25}")));
double temp = cliente.obtenerTemperatura("Madrid");
assertEquals(25, temp);
}
}
Casos de uso comunes
- Simular APIs de terceros.
- Probar manejo de errores HTTP.
- Validar reintentos y timeouts.
🔁 Caso 8: Testing Asíncrono con Awaitility
Verifica procesos que tardan en completarse o dependen de hilos.
import static org.awaitility.Awaitility.*;
@Test
void debeProcesarColaDePedidosEnMenosDe5Segundos() {
servicio.enviarPedidosPendientes();
await().atMost(5, TimeUnit.SECONDS)
.until(() -> servicio.todosProcesados());
}
Ideal para
- Jobs en segundo plano.
- Procesamiento de colas.
- Tests E2E controlados.
🧮 Caso 9: Testing de Performance Ligero
Evalúa tiempo máximo permitido de ejecución.
@Test
void debeEjecutarseEnMenosDe300ms() {
long inicio = System.nanoTime();
servicio.procesarLote(1000);
long duracion = (System.nanoTime() - inicio) / 1_000_000;
assertTrue(duracion < 300, "El procesamiento fue demasiado lento: " + duracion + " ms");
}
Mejores prácticas
- Úsalo como indicador, no como métrica absoluta.
- Evita tiempos muy ajustados que dependan del entorno.
🧭 Caso 10: Testing de Arquitectura con ArchUnit
Valida reglas de estructura en el código (por ejemplo, dependencias entre capas).
import com.tngtech.archunit.junit.AnalyzeClasses;
import com.tngtech.archunit.junit.ArchTest;
import com.tngtech.archunit.lang.syntax.ArchRuleDefinition;
@AnalyzeClasses(packages = "com.miapp")
class ArquitecturaTest {
@ArchTest
static final ArchRule serviciosNoDebenDependerDeControladores =
ArchRuleDefinition.noClasses()
.that().resideInAPackage("..servicio..")
.should().dependOnClassesThat().resideInAPackage("..controlador..");
}
Beneficio
- Asegura adherencia a la arquitectura limpia o hexagonal.
- Detecta dependencias circulares y malas prácticas.
✅ Resumen
| Tipo de Test | Herramienta Principal | Objetivo |
|---|---|---|
| Unitario | JUnit + Mockito | Lógica aislada |
| Integración | JUnit + Testcontainers | Persistencia real |
| API REST | MockMvc / RestAssured | Validar endpoints |
| Eventos / Asíncronos | Mockito / Awaitility | Flujos paralelos |
| Externos | WireMock | Simular dependencias |
| Performance | JUnit | Medir tiempos básicos |
| Arquitectura | ArchUnit | Reglas estructurales |
📚 Recursos complementarios
- Testcontainers con Docker Compose y JUnit
- WireMock para simular APIs externas
- Awaitility - Testing Asíncrono en Java
- ArchUnit - Validación de Arquitecturas
- Buenas prácticas de testing en microservicios
🧩 Patrones Avanzados de Testing y Estrategias de Mantenimiento de Test Suites
🧠 Objetivo
El propósito de esta nota es unificar los patrones avanzados de diseño de tests y las estrategias de mantenimiento que garantizan calidad, legibilidad y escalabilidad en suites de testing profesionales.
Aplicable a entornos con JUnit 5, Spring Boot, Mockito y arquitecturas DDD Domain-Driven Design o Hexagonal.
🧩 1. Patrón: Object Mother
Centraliza la creación de objetos complejos usados en múltiples tests, reduciendo duplicación.
public class UsuarioMother {
public static Usuario activo() {
Usuario u = new Usuario("Carlos", "carlos@correo.com");
u.activar();
return u;
}
public static Usuario inactivo() {
return new Usuario("Ana", "ana@correo.com");
}
}
`
Uso:
@Test
void debeEnviarCorreoAUsuarioActivo() {
Usuario usuario = UsuarioMother.activo();
servicio.enviarCorreo(usuario);
assertTrue(usuario.tieneCorreoPendiente());
}
Ventajas
- Reutilización de datos de prueba coherentes.
- Centralización de constructores repetidos.
- Fácil mantenimiento si cambian los modelos del dominio.
🧱 2. Patrón: Data Builder
Permite crear objetos de prueba de forma expresiva y configurable.
public class PedidoBuilder {
private String producto = "Taza";
private int cantidad = 1;
private double precio = 9.99;
public static PedidoBuilder unPedido() { return new PedidoBuilder(); }
public PedidoBuilder conCantidad(int cantidad) {
this.cantidad = cantidad;
return this;
}
public PedidoBuilder conPrecio(double precio) {
this.precio = precio;
return this;
}
public Pedido construir() {
return new Pedido(producto, cantidad, precio);
}
}
Uso:
@Test
void debeCalcularTotalCorrecto() {
Pedido pedido = PedidoBuilder.unPedido().conCantidad(3).conPrecio(5.0).construir();
assertEquals(15.0, pedido.total());
}
Beneficios
- Claridad semántica en los tests.
- Independencia de constructores extensos.
- Facilita agregar nuevos atributos sin romper tests.
🧪 3. Patrón: Test Fixture Reutilizable
Usar @BeforeEach o clases base de test para inicializar datos y mocks comunes.
abstract class BaseTest {
protected ServicioUsuario servicio;
protected RepositorioUsuario repo;
@BeforeEach
void setup() {
repo = mock(RepositorioUsuario.class);
servicio = new ServicioUsuario(repo);
}
}
class ServicioUsuarioTest extends BaseTest {
@Test
void debeGuardarUsuario() {
when(repo.save(any())).thenReturn(new Usuario("Juan"));
assertEquals("Juan", servicio.crearUsuario("Juan").getNombre());
}
}
Ventajas
- Reutilización de configuración.
- Menos repetición en suites grandes.
- Facilita migración entre frameworks.
⚙️ 4. Patrón: Fakes, Stubs, Mocks y Spies
| Tipo | Propósito | Ejemplo |
|---|---|---|
| Fake | Implementación simplificada real. | Fake DB en memoria |
| Stub | Devuelve respuestas predefinidas. | when(...).thenReturn(...) |
| Mock | Verifica interacciones. | verify(...) |
| Spy | Observa llamadas reales. | spy(new ServicioReal()) |
Ejemplo:
@Test
void debeGuardarUsuarioConFakeRepositorio() {
RepositorioUsuarioFake repo = new RepositorioUsuarioFake();
ServicioUsuario servicio = new ServicioUsuario(repo);
servicio.crearUsuario("Ana");
assertEquals(1, repo.count());
}
🔄 5. Patrón: Snapshot Testing en Java
Permite comparar salidas complejas con una versión “guardada” (snapshot). Se usa en serialización JSON o respuestas API.
Ejemplo con JSONAssert:
@Test
void debeCoincidirConSnapshotEsperado() throws Exception {
String jsonActual = mapper.writeValueAsString(new Producto("Taza", 10.0));
String jsonEsperado = Files.readString(Path.of("src/test/resources/snapshots/producto.json"));
JSONAssert.assertEquals(jsonEsperado, jsonActual, false);
}
Ventajas:
- Detecta regresiones visuales.
- Facilita validar respuestas API.
- Documenta el estado esperado de objetos complejos.
🧭 6. Patrón: Testing por Capas (Arquitectura Hexagonal)
Separar tests según el nivel de abstracción:
src/test/java
│
├── dominio/ → lógica pura
├── aplicacion/ → casos de uso y servicios
├── infraestructura/ → bases de datos, APIs, colas
└── presentacion/ → controladores o endpoints
Reglas
- Los tests de dominio no deben usar Spring ni dependencias externas.
- Los tests de infraestructura pueden usar Testcontainers.
- Los tests de aplicación validan integración entre capas.
⚡ 7. Patrón: Given-When-Then (GWT)
Estructura narrativa para describir casos de uso con claridad.
@Test
void debeAplicarDescuentoEnPedidoPremium() {
// Given
Pedido pedido = PedidoBuilder.unPedido().conPrecio(100).conCantidad(1).construir();
// When
pedido.aplicarDescuento(10);
// Then
assertEquals(90, pedido.total());
}
Beneficio: mejora la legibilidad y la trazabilidad de los escenarios.
🧰 8. Estrategias de Mantenimiento de Suites de Tests
🔹 Agrupación lógica por contexto
Usa @Nested para agrupar tests relacionados.
class ServicioPagoTest {
@Nested
class Validaciones {
@Test void debeRechazarPagoInvalido() { ... }
}
@Nested
class Procesamiento {
@Test void debeConfirmarPago() { ... }
}
}
🔹 Nombres descriptivos
Usar @DisplayName o convenciones expresivas:
debeGenerarFacturaCuandoPedidoEsValido()shouldThrowExceptionWhenUserNotFound()
🔹 Evitar dependencias externas
Usar mocks o fakes, no llamadas reales.
🔹 Minimizar fragilidad
Evita depender de datos de entorno, fechas, o aleatoriedad.
🔹 Separar datos de configuración
Usar archivos de recursos (.json, .yml, .csv) en /src/test/resources.
📦 9. Mantenimiento Continuo y Refactorización de Tests
- Revisar tests obsoletos cada sprint.
- Eliminar redundancias entre clases similares.
- Centralizar la lógica de asserts repetitivos en helpers.
public class Asserts {
public static void assertUsuarioActivo(Usuario u) {
assertTrue(u.isActivo());
assertNotNull(u.getFechaActivacion());
}
}
Uso:
Asserts.assertUsuarioActivo(usuario);
🔁 10. Estrategias de Ejecución en CI/CD
- Ejecutar tests unitarios en cada push (
fast feedback). - Ejecutar tests de integración en pipelines nocturnos.
- Generar reportes JUnit XML o HTML (con Allure o Surefire).
- Definir umbrales de cobertura mínima con JaCoCo.
- Integrar con SonarQube para análisis estático.
Ejemplo de pipeline:
# .github/workflows/tests.yml
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v3
with:
java-version: '21'
- run: mvn test
- run: mvn jacoco:report
🧮 11. Patrón: Golden Master Testing
Para sistemas legacy sin tests previos:
- Captura la salida actual de funciones críticas.
- Usa esa salida como referencia.
- Compara con futuras ejecuciones para detectar cambios inesperados.
Ideal cuando el comportamiento debe preservarse sin alterar la implementación.
🧩 12. Estrategias para Grandes Suites
| Estrategia | Descripción | Herramienta sugerida |
|---|---|---|
| Parallel Tests | Ejecutar tests en hilos simultáneos. | @Execution(CONCURRENT) |
| Tagging | Ejecutar subconjuntos (@Tag("slow")). |
mvn test -Dgroups=slow |
| Reportes Visuales | Reportes HTML interactivos. | Allure, ExtentReports |
| Test Impact Analysis | Solo ejecuta tests afectados. | Integración con GitHub Actions o Jenkins |
🧭 13. Anti-patrones comunes
- Overmocking: exceso de mocks que rompen la lógica real.
- Fragile tests: dependen de implementaciones internas.
- Huge Fixtures: setups demasiado grandes o lentos.
- Copy-paste testing: duplicar casos similares sin sentido.
- No asserts: tests que no verifican nada relevante.
📚 Recursos complementarios
- Arquitectura limpia en testing con JUnit
- Data Builders y Object Mothers en práctica
- Testing en entornos legacy con Golden Master
- Allure Report y métricas de test en CI/CD
- Refactorización de suites de test grandes
- Testing maintainable codebases
¿Te gusta este contenido? Suscríbete vía RSS