0

I would like to run an integration test of a single bean with resilience4j annotated method in a spring boot app. My intent is to test resiliency of bean method calls while not loading the full spring context.

The setup is as follows:

Dependencies include the following:

io.github.resilience4j:resilience4j-spring-boot2
io.github.resilience4j:resilience4j-reactor
org.springframework.boot:spring-boot-starter-aop

The resilience4j time limited spring bean with method to test:

@Service
public class FooService {

    @TimeLimiter(name = "fooTimeLimiter")
    public FooResponse foo() {
        //entertain operation that might timeout
    }
}

Configuration:

resilience4j.timelimiter.instances.fooTimeLimiter.timeoutDuration=1s

And the test:

@SpringBootTest
@ContextConfiguration(classes = FooService.class)
public class FooServiceIT {

    @Autowired
    private FooService service;
    
    @MockBean
    private Bar bar;

    @Test
    void foo_timeout() {
        //setup mocks so the operation delays the output and shall end up with timeout
        
        Assertions.assertThrows(TimeoutException.class, () -> service.foo());
    }    
}

However, the TimeLimiterAdvice.proceed() is not entertained, no timeout exception is thrown and the test fails.

Same question has been asked here: Testing SpringBoot with annotation-style Resilience4j but there is no solution.

I tried both approaches - implement FooService interface and program directly using the concrete class. With the same result.

How can I achieve the time limiter annotation is taken into account in my test?

Edit: I even tried plain spring test (no spring boot) with the following setup:

@ExtendWith(SpringExtension.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class)
public class FooServiceIT {

    @Configuration
    @Import({TimeLimiterConfiguration.class, FallbackConfiguration.class, SpelResolverConfiguration.class})
    static class ContextConfiguration {

        @Bean
        public FooService fooService() {
            //prepare bean;
        }

        @Bean
        public TimeLimiterConfigurationProperties timeLimiterConfigurationProperties() {
            return new TimeLimiterConfigurationProperties();
        }
    }

    @Autowired
    private FooService service;

    //tests...
}

Same result (i.e. no timeout exception).

miro
  • 131
  • 2
  • 9

1 Answers1

1

When dealing with SpringBootTest and @CircuitBreaker, it was sufficient to add @EnableAspectJAutoProxy annotation to the test. After this change, the CircuitBreakerAspect was entertained and the test behaves as expected.

In order to make @TimeLimiter working as expected, one need to add @Bulkhead annotation to the method as well.

The updated method looks as follows:

@Bulkhead(name = "fooBulkhead", type = Type.THREADPOOL)
@CircuitBreaker(
        name = "fooCircuitBreaker",
        fallbackMethod = "fooFallback"
)
@TimeLimiter(
        name = "fooTimeLimiter"
)
public CompletableFuture<FooResponse> foo() {
    //...
}

and the test:

@SpringBootTest(classes = FooService.class)
@EnableAspectJAutoProxy
@Import(value = {CircuitBreakerAutoConfiguration.class, TimeLimiterAutoConfiguration.class, BulkheadAutoConfiguration.class})
public class FooServiceIT {
    //...
}
miro
  • 131
  • 2
  • 9