guava-retrying es una extensión de la biblioteca Guava que permite definir estrategias de reintento configurables para llamadas a funciones, como servicios remotos con disponibilidad intermitente. Este artículo explica cómo desarrollar pruebas unitairas robustas utilizando JUnit para validar la lógica de reintento.
Preparación del entorno de pruebas
Asegúrese de incluir las dependencias de JUnit y guava-retrying en el proyecto. Las pruebas se organizan en paquetes como com.github.retry.test y abarcan clases específicas para cada componente.
Clases de prueba esenciales:
RetryerConstructorTest: valida la configuración del constructor de reintentos.PauseStrategyTest: verifica los cálculos de tiempo entre reintentos.HaltStrategyTest: prueba las condiciones de parada.AttemptTimerTest: examina los límites de tiempo por intento.
Pruebas de condiciones de reintento
Evalúe escenarios donde se deben activar reintentos, como ante excepciones o resultados inválidos. Ejemplo modificado:
@Test
public void reintentoPorExcepcionIo() throws ExecutionException, RetryException {
Callable<string> operacion = simularErrorRedEnTresIntentos();
Retryer<string> retryer = RetryerBuilder.<String>nuevoConstructor()
.reintentarSiExcepcion(IOException.class)
.construir();
String resultado = retryer.llamar(operacion);
assertNotNull(resultado);
}</string></string>
Validación de estrategias de pausa
Las estrategias de pausa determinan los intervalos entre reintentos. Pruebe diferentes configuraciones, como pausa fija o exponencial:
@Test
public void pausaExponencialConLimite() {
PauseStrategy exponencial = PauseStrategies.pausaExponencial(50, TimeUnit.MILLISECONDS);
long primerTiempo = exponencial.calcularTiempoDeSueño(intentoFallido(1, 0L));
long segundoTiempo = exponencial.calcularTiempoDeSueño(intentoFallido(2, 0L));
assertTrue(segundoTiempo > primerTiempo);
// Verificar que el tiempo no exceda un máximo predefinido
long tiempoFinal = exponencial.calcularTiempoDeSueño(intentoFallido(10, 0L));
assertEquals(50, tiempoFinal);
}
Pruebas de estrategias de parada
Controle cuándo finalizan los reintentos. Implemente pruebas para límites basados en intentos o tiempo:
@Test
public void paradaPorLimiteDeIntentos() {
HaltStrategy halt = HaltStrategies.paradaDespuesDeIntentos(4);
assertFalse(halt.deberiaParar(intento(2, 0)));
assertTrue(halt.deberiaParar(intento(4, 0)));
}
Técnicas avanzadas de prueba
Combine múltiples estrategias y pruebe funcionalidades como escuchas de reintento. Ejemplo para estrategias combinadas:
@Test
public void multiplesCondicionesDeReintento() throws ExecutionException, RetryException {
Callable<Boolean> tarea = tareaConFallasMixtas();
Retryer<Boolean> retryer = RetryerBuilder.<Boolean>nuevoConstructor()
.reintentarSiResultado(Predicates.esNulo())
.reintentarSiExcepcion(TimeoutException.class)
.conEstrategiaDeParada(HaltStrategies.paradaDespuesDeIntentos(5))
.construir();
boolean exito = retryer.llamar(tarea);
assertTrue(exito);
}
Para probar escuchas de reintento, registre eventos durante la ejecución:
@Test
public void escuchaRegistraIntentos() throws Exception {
List<Intento<?>> intentos = new ArrayList<>();
EscuchaReintento listener = new EscuchaReintento() {
@Override
public <V> onReintento(Intento<V> intento) {
intentos.add(intento);
}
};
Callable<String> callable = tareaConRetraso();
Retryer<String> retryer = RetryerBuilder.<String>nuevoConstructor()
.reintentarSiResultado(String::isEmpty)
.conEscucha(listener)
.construir();
retryer.llamar(callable);
assertEquals(3, intentos.size());
}
Mejores prácticas
Use @BeforeEach y @AfterEach para aislar el estado de las pruebas. Implemente pruebas parametrizadas para cubrir diferentes escenarios:
@ParameterizedTest
@CsvSource({"2, 100", "4, 200", "6, 400"})
void probarPausaFija(int intentos, long esperado) {
PauseStrategy pause = PauseStrategies.pausaFija(esperado, TimeUnit.MILLISECONDS);
long actual = pause.calcularTiempoDeSueño(intentoFallido(intentos, 0));
assertEquals(esperado, actual);
}
Enfoque en casos límite, como reintentos máximos o tiempos de espera extremos.
Estructura de pruebas del proyecto
Las pruebas se organizan en archivos dedicados, como:
RetryerConstructorTest.javaPauseStrategyTest.javaHaltStrategyTest.javaAttemptTimerTest.java
Cada clase se enfoca en un componente, facilitando el mantenimiento.