原创

API 的单元测试 Resilience4j 断路器

温馨提示:
本文最后更新于 2024年04月12日,已超过 48 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我

I want to implement CircuitBreaker pattern in an spring boot application.I have two APIs and each API has its own configuration. I defined these two configuration methods in application class. Also, in application class I defined a Spring Bean of type CustomCircuitBreakerFactory. This type is a custom class and will be created to manage and configure circuit breakers for APIs. This spring bean takes three parameters in its constructor:

@SpringBootApplication(scanBasePackages = {"com.company.*"})
public class ApiApplication {
    public static void main(String[] args) {
        SpringApplication.run(ApiApplication.class, args);
    }

    @Bean
    @Qualifier("api1CircuitBreakerConfig")
    public CircuitBreakerConfig api1CircuitBreakerConfig() {

        return CircuitBreakerConfig.custom()
            .failureRateThreshold(50)
            .waitDurationInOpenState(Duration.ofSeconds(10))
            .permittedNumberOfCallsInHalfOpenState(10)
            .slidingWindowSize(100)
            .minimumNumberOfCalls(20)
            .recordExceptions(RuntimeException.class, CallNotPermittedException.class)
            .build();
    }

    @Bean
    @Qualifier("api2CircuitBreakerConfig")
    public CircuitBreakerConfig api2CircuitBreakerConfig() {
        return CircuitBreakerConfig.custom()
            .failureRateThreshold(40)
            .waitDurationInOpenState(Duration.ofSeconds(10))
            .permittedNumberOfCallsInHalfOpenState(10)
            .slidingWindowSize(150)
            .minimumNumberOfCalls(30)
            .recordExceptions(RuntimeException.class, CallNotPermittedException.class)
            .build();

    }

    @Bean
    public CustomCircuitBreakerFactory customCircuitBreakerFactory(
        ReactiveResilience4JCircuitBreakerFactory circuitBreakerFactory,
        CircuitBreakerConfig api1CircuitBreakerConfig,
        CircuitBreakerConfig api2CircuitBreakerConfig) {
        return new CustomCircuitBreakerFactory(circuitBreakerFactory, api1CircuitBreakerConfig, api2CircuitBreakerConfig);
    }
}

It is the implementation of CustomCircuitBreakerFactory. It is an component class to configure and manage the circuit breakers. It takes three parameters in its constructor has two methods to create and configure instances of circuit breakers for each of underlying APIs.

import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cloud.circuitbreaker.resilience4j.ReactiveResilience4JCircuitBreakerFactory;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class CustomCircuitBreakerFactory {

    private final ReactiveResilience4JCircuitBreakerFactory circuitBreakerFactory;
    private final CircuitBreakerConfig api1CircuitBreakerConfig;
    private final CircuitBreakerConfig api2CircuitBreakerConfig;


    public CustomCircuitBreakerFactory(
        ReactiveResilience4JCircuitBreakerFactory circuitBreakerFactory,
        @Qualifier("api1CircuitBreakerConfig") CircuitBreakerConfig api1CircuitBreakerConfig,
        @Qualifier("api2CircuitBreakerConfig") CircuitBreakerConfig api2CircuitBreakerConfig) {
        this.circuitBreakerFactory = circuitBreakerFactory;
        this.api1CircuitBreakerConfig= api1CircuitBreakerConfig;
        this.api2CircuitBreakerConfig= api2CircuitBreakerConfig;
    }


    public CircuitBreaker createApi1CircuitBreaker() {
        return (CircuitBreaker) circuitBreakerFactory.create("api1");
    }

    public CircuitBreaker createApi2CircuitBreaker() {
        return (CircuitBreaker) circuitBreakerFactory.create("api2");
    }
}

I have two services and search method in each service works with circuit breaker pattern and uses its configuration.

@Service
public class Api1Search {

    private static final String SERVICE_NAME = "api1";

    @Override
    @CircuitBreaker(name = SERVICE_NAME, fallbackMethod = "fallback")
    public List<String> search(String input) {
       // logic is implemented
    }


    public List<String> fallback(String input) {
        // logic is implemented
    }
}



@Service
public class Api2Search{

    private static final String SERVICE_NAME = "api2";

    @Override
    @CircuitBreaker(name = SERVICE_NAME, fallbackMethod = "fallback")
    public List<String> search(String input) {  
       // logic is implemented
    }

public List<String> fallback(String input) {
        // logic is implemented
    }
}
    
}

I want to write some unit tests to check different scenarios of circuit breaker. I don't have any Idea how to implement these unit tests. For example, I implemented this method but when I set the state of CircuitBreaker to be open, the method search runs normally, while I expect that the fallback method run. How can I implement the right methods to test?

@ExtendWith(MockitoExtension.class)
public class Api1SearchTest {
    @Mock
    private CustomCircuitBreakerFactory circuitBreakerFactory;

    @InjectMocks
    private Api1Search api1Search;

    @Test
    public void testMethod_CircuitBreakerOpen()  {
        CircuitBreaker circuitBreaker = Mockito.mock(CircuitBreaker.class);
        when(circuitBreaker.getState()).thenReturn(CircuitBreaker.State.OPEN);
        when(circuitBreakerFactory.createApi1CircuitBreaker()).thenReturn(circuitBreaker);

     

        List <String> result= api1Search.search("input");

        assertEquals(result.size(),0);
    }
正文到此结束
热门推荐
本文目录