Search code examples
spring-bootjunitmockitoresilience4jresilience4j-retry

Resilience4j + Spring boot @Retry not working with async methods


I have an @Async method where I'm trying to add @Retry but the fallback method is never being executed when an exception occurs. I am also trying to test this mocking that an exception is thrown, but as it never goes to the fallback method, it never success.

This is my code:

@Retry(name = "insertarOperacionPendienteService", fallbackMethod = "fallbackInsertarOperacionPendiente")
@Override
@Async
public CompletableFuture<String> insertarOperacionPendiente(final OperacionPendienteWeb operacionPendienteWeb)  throws InterruptedException, ExecutionException {
    StringBuilder debugMessage = new StringBuilder("[insertarOperacionPendiente] Operacion pendiente a insertar en BB.DD.: ").append(operacionPendienteWeb);
    CompletableFuture<String> result = new CompletableFuture<>();
    HttpEntity<List<OperacionPendienteWeb>> entity = new HttpEntity<>();
    UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("url");
    try {
        rest.exchange(builder.toUriString(), HttpMethod.POST, entity, Void.class);
    } catch (HttpClientErrorException | HttpServerErrorException e) {
        result.completeExceptionally(e);
    } catch (Exception e) {
        result.completeExceptionally(e);
    }   

    result.complete("OK");
    return result;
}

public CompletableFuture<String> fallbackInsertarOperacionPendiente(Exception e) {
       System.out.println("HI");
       throw new InternalServerErrorDarwinException("Error al insertar la operacion pendiente.");
}

Test:

@Test(expected = InternalServerErrorDarwinException.class)
public void procesarOperacionPendienteKO1() throws InterruptedException, ExecutionException, ParseException {
    when(rest.exchange(Mockito.anyString(), 
            Mockito.any(HttpMethod.class), 
            Mockito.any(HttpEntity.class), 
            Mockito.eq(Void.class)))
    .thenThrow(new NullPointerException());

    this.operacionesPendientesService.insertarOperacionPendiente(obtenerOperacionPendienteWeb()).get(); 

    verify(rest, timeout(100).times(1)).exchange(Mockito.anyString(), 
            Mockito.any(HttpMethod.class), 
            Mockito.any(HttpEntity.class), 
            Mockito.eq(Void.class));

}

Am I missing something?

Thanks!


Solution

  • Your code looks like this:

    try {
        rest.exchange(builder.toUriString(), HttpMethod.POST, entity, Void.class);
    } catch (HttpClientErrorException | HttpServerErrorException e) {
        result.completeExceptionally(e);
    } catch (Exception e) {
        result.completeExceptionally(e);
    }   
    
    result.complete("OK");
    

    So on the last line you always set the result to complete!

    Change it to:

    try {
        rest.exchange(builder.toUriString(), HttpMethod.POST, entity, Void.class);
        result.complete("OK");
    } catch (HttpClientErrorException | HttpServerErrorException e) {
        result.completeExceptionally(e);
    } catch (Exception e) {
        result.completeExceptionally(e);
    }