如何为使用@Circuitbreaker 注释的方法创建单元测试
How to create unit tests for methods annotated with @Circuitbreaker
我在我的项目中使用 Spring Boot2 启动程序 (https://resilience4j.readme.io/docs/getting-started-3) 实现了 resilience4j。
我用 @CircuitBreaker 注释了一个方法,该方法使用 http 客户端调用外部服务,并且断路器工作正常 - 包括其回退。
我想为它添加单元测试,但是当我 运行 一个试图模拟回退的测试时,没有任何反应 - 异常被抛出但没有被断路器机制处理。
我找到了一些使用其指标的示例,但它对我的情况没有用。
有什么想法吗?
这是我的客户的片段:
@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";
}
您不应该在单元测试中测试@CircuitBreaker,因为它涉及多个class。而是使用集成测试。
作为andres and pvpkiranmentioned/explained,我不得不添加一个集成测试。
您基本上可以将 @SpringBootTest 注释添加到您的测试 class,它将 bootstrap 一个带有 spring 上下文的容器。
我还自动连接了 CircuitBreakerRegistry,以便在每次测试前重置断路器,这样我就可以保证测试干净。对于 mocking/spying/verifying,我使用了 spring 启动测试启动器 (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));
}
}
我希望没有编译错误,因为我不得不删除一些不相关的东西。
我在我的项目中使用 Spring Boot2 启动程序 (https://resilience4j.readme.io/docs/getting-started-3) 实现了 resilience4j。
我用 @CircuitBreaker 注释了一个方法,该方法使用 http 客户端调用外部服务,并且断路器工作正常 - 包括其回退。
我想为它添加单元测试,但是当我 运行 一个试图模拟回退的测试时,没有任何反应 - 异常被抛出但没有被断路器机制处理。
我找到了一些使用其指标的示例,但它对我的情况没有用。
有什么想法吗?
这是我的客户的片段:
@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";
}
您不应该在单元测试中测试@CircuitBreaker,因为它涉及多个class。而是使用集成测试。
作为andres and pvpkiranmentioned/explained,我不得不添加一个集成测试。
您基本上可以将 @SpringBootTest 注释添加到您的测试 class,它将 bootstrap 一个带有 spring 上下文的容器。
我还自动连接了 CircuitBreakerRegistry,以便在每次测试前重置断路器,这样我就可以保证测试干净。对于 mocking/spying/verifying,我使用了 spring 启动测试启动器 (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));
}
}
我希望没有编译错误,因为我不得不删除一些不相关的东西。