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).
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 {
//...
}