Я использую Spring бутс 1.4.0.RELEASE
с spring-boot-starter-batch
, spring-boot-starter-aop
и spring-retry
Передразнивали Spring @Service, который имеет @Retryable аннотации методов не удается с UnfinishedVerificationException
У меня есть тест интеграции Spring, который имеет @Service
, который высмеивал во время выполнения. Я заметил, что если класс @Service
содержит любые аннотации @Retryable
по его методам, то он, кажется, вмешивается в Mockito.verify()
, я получаю UnfinishedVerificationException
. Я полагаю, это должно быть связано с spring-aop
? Если я прокомментирую все аннотации @Retryable
в @Service
, тогда проверьте работу снова.
Я создал github project, который демонстрирует эту проблему.
Он терпит неудачу в sample.batch.MockBatchTestWithRetryVerificationFailures.batchTest()
на validateMockitoUsage();
С чем-то вроде:
12:05:36.554 [main] DEBUG org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener - After test method: context [[email protected] testClass = MockBatchTestWithRetryVerificationFailures, testInstance = [email protected], testMethod = [email protected], testException = org.mockito.exceptions.misusing.UnfinishedVerificationException:
Missing method call for verify(mock) here:
-> at sample.batch.service.MyRetryService$$FastClassBySpringCGLIB$$7573ce2a.invoke(<generated>)
Example of correct verification:
verify(mock).doSomething()
Однако у меня есть еще один класс (sample.batch.MockBatchTestWithNoRetryWorking.batchTest()
) с издевались @Service
, который не имеет каких-либо @Retryable
аннотацию и проверить работает отлично.
Что я делаю неправильно?
В моей pom.xml У меня есть следующие:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.0.RELEASE</version>
</parent>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
...
Тогда все связанные классы Java
@SpringBootApplication
@EnableBatchProcessing
@Configuration
@EnableRetry
public class SampleBatchApplication {
@Autowired
private JobBuilderFactory jobs;
@Autowired
private StepBuilderFactory steps;
@Autowired
private MyRetryService myRetryService;
@Autowired
private MyServiceNoRetry myServiceNoRetry;
@Bean
protected Tasklet tasklet() {
return new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution,
ChunkContext context) {
myServiceNoRetry.process();
myRetryService.process();
return RepeatStatus.FINISHED;
}
};
}
@Bean
public Job job() throws Exception {
return this.jobs.get("job").start(step1()).build();
}
@Bean
protected Step step1() throws Exception {
return this.steps.get("step1").tasklet(tasklet()).build();
}
public static void main(String[] args) throws Exception {
// System.exit is common for Batch applications since the exit code can be used to
// drive a workflow
System.exit(SpringApplication
.exit(SpringApplication.run(SampleBatchApplication.class, args)));
}
@Bean
ResourcelessTransactionManager transactionManager() {
return new ResourcelessTransactionManager();
}
@Bean
public JobRepository getJobRepo() throws Exception {
return new MapJobRepositoryFactoryBean(transactionManager()).getObject();
}
}
@Service
public class MyRetryService {
public static final Logger LOG = LoggerFactory.getLogger(MyRetryService.class);
@Retryable(maxAttempts = 5, include = RuntimeException.class, backoff = @Backoff(delay = 100, multiplier = 2))
public boolean process() {
double random = Math.random();
LOG.info("Running process, random value {}", random);
if (random > 0.2d) {
throw new RuntimeException("Random fail time!");
}
return true;
}
}
@Service
public class MyServiceNoRetry {
public static final Logger LOG = LoggerFactory.getLogger(MyServiceNoRetry.class);
public boolean process() {
LOG.info("Running process that doesn't do retry");
return true;
}
}
@ActiveProfiles("Test")
@ContextConfiguration(classes = {SampleBatchApplication.class, MockBatchTestWithNoRetryWorking.MockedRetryService.class}, loader = AnnotationConfigContextLoader.class)
@RunWith(SpringRunner.class)
public class MockBatchTestWithNoRetryWorking {
@Autowired
MyServiceNoRetry service;
@Test
public void batchTest() {
service.process();
verify(service).process();
validateMockitoUsage();
}
public static class MockedRetryService {
@Bean
@Primary
public MyServiceNoRetry myService() {
return mock(MyServiceNoRetry.class);
}
}
}
@ActiveProfiles("Test")
@ContextConfiguration(classes = { SampleBatchApplication.class,
MockBatchTestWithRetryVerificationFailures.MockedRetryService.class },
loader = AnnotationConfigContextLoader.class)
@RunWith(SpringRunner.class)
public class MockBatchTestWithRetryVerificationFailures {
@Autowired
MyRetryService service;
@Test
public void batchTest() {
service.process();
verify(service).process();
validateMockitoUsage();
}
public static class MockedRetryService {
@Bean
@Primary
public MyRetryService myRetryService() {
return mock(MyRetryService.class);
}
}
}
EDIT: Обновленный вопрос и код на основе образца проекта я поставил вместе, чтобы показать проблема.
Можете ли вы уточнить код – kuhajeyan
@kuhajeyan, конечно, я сделал это сейчас. Надеюсь, я включил достаточно информации –
@kuhajeyan обновлен снова, но с рабочим примером в github, который вы можете попробовать –