순단과 같은 네트워크 에러가 가끔 발생하는 부분에 대한 처리를 어떻게 해야 할지 고민하는 도중 spring-retry를 발견하게 되었고, 간편하게 retry 로직을 수행할 수 있을 것 같아 학습하고 적용해 보았다.
Spring project
Dependency
implementation("org.springframework.retry:spring-retry")
implementation("org.springframework:spring-aspects")
@EnableRetry
@Configuration
@EnableRetry
public class MyConfig {
...
}
@Retryable
@Retryable(
include = RuntimeException.class,
exclude = DataAccessException.class,
maxAttempts = 5,
backoff = @Backoff(2000L),
recover = "recoverTest"
)
public void test(Dto dto) {
// ...
}
@Recover
public void recoverTest(RuntimeException e, Dto dto) {
// ...
}
@Retryable attributes
- maxAttempts
- backoff
- 다음 수행까지 delay time
- defaults 1000ms
- include(retryFor)
- retry를 수행할 특정 에러 지정
- defaults : all exception
- exclude
- recover
- maxAttempts 이후에 도달했을 때 수행하는 로직 지정
Test Code
maxRetryAttempt를 3번이라고 설정해 둔 코드를 테스트한다고 하면
BDDMockito를 통해서 2번은 Exception을 던지고 한 번은 성공 처리를 할 수 있다
최종적으로 test method의 로직이 3번 호출되었는지 확인하는 식으로 테스트를 작성할 수 있다.
@Test
void test() {
// given
Dto dto = generateDto();
BDDMockito.willThrow(new RuntimeException("", new Exception()))
.willThrow(new RuntimeException("", new Exception()))
.willDoNothing()
.given(myRepository).test(Mockito.any());
// when
sut.test(dto);
// then
Mockito.verify(myRepository, Mockito.times(3)).test(Mockito.any());
}
이와 반대로 지속적으로 에러가 발생하도록 하여 테스트 코드를 작성할 수도 있다.
지속적으로 에러가 발생하니 검증하고자 하는 test method는 exception을 던질 거라고 assertion을 작성하면 된다.
@Test
void test() {
// given
Dto dto = generateDto();
BDDMockito.willThrow(new RuntimeException("", new Exception()))
.given(myRepository).test(Mockito.any());
// when
Assertions.assertThatThrownBy(() -> sut.test(dto))
.isInstanceOf(RuntimeException.class);
// then
Mockito.verify(myRepository, Mockito.times(3)).test(Mockito.any());
}