I implemented resilience4j in my project using the Spring Boot2 starter (https://resilience4j.readme.io/docs/getting-started-3).
I annotated a method with @CircuitBreaker that uses http client for calling an external service and the circuit breaker is working fine - including its fallback.
I'd like to add unit tests for it but when I run a test trying to simulate the fallback, nothing happens - the exception is thrown but is not handled by the circuit breaker mechanism.
I've found some examples using its metrics but it is not useful in my case.
Any thoughts?
Here is a snippet of my client:
@CircuitBreaker(name = "MY_CICUIT_BREAKER", fallbackMethod = "fallback")
public ResponseEntity<String> search(String value) {
ResponseEntity<String> responseEntity = restTemplate.exchange(
searchURL,
HttpMethod.GET,
new HttpEntity(new HttpHeaders()),
String.class,
value);
}
public ResponseEntity<String> fallback(String value, ResourceAccessException ex) {
return "fallback executed";
}
Well, this is not a unit test. When we use the @SpringBootTest annotation, Spring loads up an application context for the test. In practice, we have started the whole application only to autowire the OrderService into the test.
Spring Boot simplifies unit testing with SpringRunner . It is also easier to write unit tests for REST Controller with MockMVC . For writing a unit test, Spring Boot Starter Test dependency should be added to your build configuration file (pom.
As andres and pvpkiran mentioned/explained, I had to add a integration test.
You can achieve that basically adding @SpringBootTest annotation to your test class, it will bootstrap a container with spring context on it.
I also autowired CircuitBreakerRegistry in order to reset the circuit breaker before each test so I could guarantee a clean test. For mocking/spying/verifying I used Mockito from spring boot test starter (spring-boot-starter-test).
Here is how I managed to test the fallbacks methods:
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = Application.class)
public class RestClientIntegrationTest {
private final String SEARCH_VALUE = "1234567890";
@MockBean( name = "myRealRestTemplateName")
private RestTemplate restTemplate;
@SpyBean
private MyRestClient client;
@Autowired
private CircuitBreakerRegistry circuitBreakerRegistry;
@BeforeEach
public void setUp() {
circuitBreakerRegistry.circuitBreaker("MY_CIRCUIT_BREAKER_NAME").reset();
}
@Test
public void should_search_and_fallback_when_ResourceAccessException_is_thrown() {
// prepare
when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class), eq(String.class), eq(SEARCH_VALUE)))
.thenThrow(ResourceAccessException.class);
String expectedResult = "expected result when fallback is called";
// action
String actualResult = client.search(SEARCH_VALUE);
// assertion
verify(client).fallback(eq(SEARCH_VALUE), any(ResourceAccessException.class));
assertThat(actualResult, is(expectedResult));
}
}
I hope there is no compilation error since I had to remove some non-relevant stuff.
You shouldn't test @CircuitBreaker in a unit test as it involves more than one class. Rather use an integration test.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With