Spring Boot @Retryable模拟测试

时间:2019-12-29 06:23:37

标签: spring-boot unit-testing mockito

我有如下代码块:

    @Service
    ExecutorService {

    @Autowired
    IAccountService accountService;

        @Retryable(maxAttempts = 3, value = {DataIntegrityViolationException.class}, backoff = @Backoff(delay = 1000, multiplier = 2))
        public void execute(RequestDto reqDto)
        {
            Account acc = accountService.getAccount(reqDto.getAccountId);
            ...
        }
    }

在Mockito测试中,我只想看到调用方法3次,如预期的那样。

    @RunWith(SpringRunner.class)
    public class CampaignExecuterServiceTest
    {
        private static final Long ACCOUNT_ID = 1L;

        @InjectMocks
        private ExecutorService executorService;

        @Mock
        private IAccountService accountService;

        @Test
        public void execute_success()
        {
            Account account = new Account(ACCOUNT_ID, null, null, null, null);
            RequestDto reqDto = new RequestDto();
            when(accountService.getAccount(any())).thenThrow(DataIntegrityViolationException.class);
            executorService.execute(reqDto);
            verify(executorService, times(3)).execute(any());
        }
    }

测试仅引发异常。但是我希望它能调用3次。

1 个答案:

答案 0 :(得分:1)

这里有一些问题。

1)@SpringBootTest需要创建一个Spring Boot应用运行程序,它将拦截您的可重试方法。 JUnit不会自己这样做。另外,classes参数必须是您的主要类,而不是MainApplication或可以作为Spring引导应用程序运行的类的子集。

2)ExecutorService必须用@Autowired注释,因此它将是测试正在创建的Spring Boot应用程序中的bean。

3)IAccountService必须是@MockBean,这是测试Spring引导环境将知道如何在ExecutorService中使用模拟类而不是真实bean的方式。

4)在测试中,第三个模拟调用需要返回有效结果,否则将引发异常,并且测试将失败。或者,您可以在测试中捕获异常。

5)ExecutorService不是模拟或间谍,因此verify在运行时不会接受为arg,但是accountService是模拟,因此只需断言3次即可。

另一个注意事项是,在Spring引导配置中或MainApplication的某处,您必须具有@EnableRetry批注。

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = MainApplication.class)
public class CampaignExecuterServiceTest {
    private static final Long ACCOUNT_ID = 1L;

    @Autowired
    private ExecutorService executorService;

    @MockBean
    private IAccountService accountService;

    @Test
    public void execute_success() {
        Account account = new Account(ACCOUNT_ID, null, null, null, null);;
        RequestDto reqDto = new RequestDto();
        when(accountService.getAccount(any()))
                .thenThrow(DataIntegrityViolationException.class)
                .thenThrow(DataIntegrityViolationException.class)
                .thenReturn(account);
        executorService.execute(reqDto);
        verify(accountService, times(3)).getAccount(any());
    }
}