I am asking a question similar to this one, and I have tried many different ways of acquiring an existing circuit breaker, created via annotation, within my integration test.
I tried Autowiring in both the main class and test class:
@Autowired
CircuitBreakerRegistry cbr;
But for whatever reason, that doesn't do the trick. I get:
Field circuitBreakerRegistry in example.my.class.ClassWithCircuitBreaker required a bean of type 'io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry' that could not be found.
So I tried this:
@Autowired
private ReactiveResilience4JCircuitBreakerFactory reactiveResilience4JCircuitBreakerFactory;
That succeeds in wiring that object, but when I call reactiveResilience4JCircuitBreakerFactory.getCircuitBreakerRegistry().circuitBreaker("myCB")
, it appears to be creating a new instance rather than retrieving an existing one. When I run the test in debug mode, I can see it hit here:
// io.github.resilience4j.circuitbreaker.CircuitBreaker
static CircuitBreaker of(String name, CircuitBreakerConfig circuitBreakerConfig, Map<String, String> tags) {
return new CircuitBreakerStateMachine(name, circuitBreakerConfig, tags);
}
That earlier question-and-answer doesn't really answer the question of how to find an existing CB when they are externally configured and created via the @CircuitBreaker
annotation. The reason for doing this is twofold - 1) get the existing CB and disable it if a custom configuration is set to true
, and, 2) validate that behavior inside an integration test.
The class being tested looks in part like this:
@Component
@Slf4j
@RequiredArgsConstructor
public class ClassWithCircuitBreaker implements SomeInterface {
private static final String CIRCUIT_BREAKER_NAME = "myCB";
// other stuff, then...
@Autowired
CircuitBreakerRegistry circuitBreakerRegistry;
// annotated method
@Override
@CircuitBreaker(name = CIRCUIT_BREAKER_NAME, fallbackMethod = "fallback")
public ThisStuff doStuff(String term, String id) {
// do stuff
}
private ThisStuff fallback(String term, String id, Exception e) {
log.error("exception while calling doStuff term: {}, id: {}, other thing: {}, exception: {}",
queryTerm,
requestId,
classWithCbConfig.getOtherThing(),
e
);
return ThisStuff.DEFAULT_STUFF;
}
@PostConstruct
private void disableCircuitBreakerMaybe() {
io.github.resilience4j.circuitbreaker.CircuitBreaker cb = circuitBreakerRegistry.circuitBreaker(CIRCUIT_BREAKER_NAME);
if(classWithCbConfig.isDisableCircuitBreaker()) {
log.info("disabling circuit breaker {}", cb.getName());
cb.transitionToDisabledState();
} else {
log.info("circuit breaker {} is in current state {}", CIRCUIT_BREAKER_NAME, cb.getState().name());
}
}
}
In the test, I am forcing the call to doStuff
to throw an exception. This should result in failed calls being recorded when the CB is not disabled, but I see this output:
log.info("failed metrics :: {}", circuitBreakerRegistry.circuitBreaker("myCB").getMetrics().getNumberOfFailedCalls());
log.info("success metrics :: {}", circuitBreakerRegistry.circuitBreaker("myCB").getMetrics().getNumberOfSuccessfulCalls());
2023-04-14T09:03:24,695 [main] INFO example.my.cb.CircuitBreakerTest - failed metrics :: 0
2023-04-14T09:03:24,696 [main] INFO example.my.cb.CircuitBreakerTest - success metrics :: 0
Project dependencies:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.6.6</version>
</dependency>
I have been frustrated by this for the last 24 hours, any help would be much appreciated.
Edit #1
The test class is annotated thusly, before someone suggests the SpringBootTest
annotation:
@Slf4j
@SpringBootTest(
classes = {
TestConfig.class,
// various other test configs here...
CircuitBreakerRegistry.class,
ReactiveResilience4JCircuitBreakerFactory.class
}
)
@ActiveProfiles("test")
@ExtendWith(SpringExtension.class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
Update #1
Adding this to the import annotation succeeded in adding the CircuitBreakerRegistry
to Spring:
@Import(
{
// ..all the other configs
CircuitBreakerAutoConfiguration.class
}
)
Technically, I think it added all of the classes from the io.github.resilience4j.circuitbreaker.autoconfigure
package. At any rate, problem #1 is solved! The only issue I have left (I think), is that the fallback method is not being invoked even when the base method throws an exception. The metrics are still showing as 0 failed calls.