【问题标题】:How to create unit tests for methods annotated with @Circuitbreaker如何为使用 @Circuitbreaker 注释的方法创建单元测试
【发布时间】:2020-07-10 21:15:26
【问题描述】:

我在我的项目中使用 Spring Boot2 启动器 (https://resilience4j.readme.io/docs/getting-started-3) 实现了弹性 4j。

我用 @CircuitBreaker 注释了一个方法,该方法使用 http 客户端调用外部服务,并且断路器工作正常 - 包括它的后备。

我想为它添加单元测试,但是当我运行一个测试来模拟回退时,什么都没有发生 - 异常被抛出,但没有被断路器机制处理。

我找到了一些使用其指标的示例,但在我的情况下没有用。

有什么想法吗?

这是我客户的 sn-p:

@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";
    }

【问题讨论】:

  • 这个方法怎么叫搜索?
  • 只需从服务调用client.search(value),由控制器调用
  • 这就是问题所在。如果您直接调用该服务,那么 spring magic(aspects) 就不会出现在图片中。因此断路器功能不起作用。在正常执行期间,当流从一个文件流向另一个文件(控制器到服务类)时,spring 会拦截调用并做很多事情。Becos 整个事情都有效。如果你直接打电话,那么它不起作用。调用必须从 spring bean 到 spring bean
  • 嗯...有道理。然后我将使用集成测试!感谢您的及时支持!
  • @user2541537 你是如何进行这个集成测试的?我遇到了同样的问题,如果你能分享对你有用的东西,你是如何设置集成测试的,等等......谢谢

标签: java spring-boot unit-testing aop resilience4j


【解决方案1】:

您不应该在单元测试中测试@CircuitBreaker,因为它涉及多个类。而是使用集成测试。

【讨论】:

    【解决方案2】:

    正如andrespvpkiran 提到/解释的那样,我必须添加一个集成测试。

    您基本上可以在您的测试类中添加@SpringBootTest 注释来实现这一点,它将引导一个带有弹簧上下文的容器。

    我还自动连接了 CircuitBreakerRegistry,以便在每次测试之前重置断路器,这样我就可以保证测试干净。对于模拟/间谍/验证,我使用了 spring boot test starter (spring-boot-starter-test) 中的 Mockito。

    这是我设法测试后备方法的方法:

    @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));
        }
    
    }
    

    我希望没有编译错误,因为我不得不删除一些不相关的东西。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-10-20
      • 2020-02-14
      • 2017-09-13
      • 1970-01-01
      • 1970-01-01
      • 2023-02-09
      • 2018-10-17
      相关资源
      最近更新 更多