I created a testclass for a Job in Spring Batch, which works fine. However I have to add an extension, which extends SpringExtends, and then I get an error saying nested exception is java.lang.IllegalStateException: The field de.orgen.batchprocessing2.DownloadService de.orgen.batchprocessing2.chunkJobsTest.downloadService cannot have an existing value
My test is:
@SpringBootTest
@SpringJUnitConfig
@ExtendWith(MyExtension.class)
public class chunkJobsTest {
@Autowired
private Job secondJob;
@Autowired
private JobLauncher jobLauncher;
@MockBean
DownloadService downloadService;
@Autowired
private JobRepository jobRepository;
private JobLauncherTestUtils jobLauncherTestUtils;
@BeforeEach
public void initializeJobLauncherTestUtils() {
// MockitoAnnotations.initMocks(this);
this.jobLauncherTestUtils = new JobLauncherTestUtils();
this.jobLauncherTestUtils.setJobLauncher(jobLauncher);
this.jobLauncherTestUtils.setJobRepository(jobRepository);
this.jobLauncherTestUtils.setJob(secondJob);
}
@Test
void testing(){
Map<String, JobParameter> param = new HashMap<>();
param.put("test", new JobParameter("test1"));
when(this.downloadService.downloadFile()).thenReturn("mocked file");
JobExecution jobExecution = jobLauncherTestUtils.launchStep("step1Chunk"
, new JobParameters(param));
}
}
My ConfigurationClass:
@Configuration
@EnableBatchProcessing
public class ChunkJobs {
@Autowired
JobRepository jobRepository;
@Autowired
private FirstItemReader reader;
@Autowired
private FirstItemProcessor processor;
@Autowired
private FIrstItemWriter writer;
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
@Bean
public Job secondJob(Step step1Chunk){
return jobBuilderFactory.get("myJob2")
.incrementer(new RunIdIncrementer())
.start(step1Chunk)
// .next(step2)
.build();
}
@Bean("step1Chunk")
public Step step1Chunk(){
return stepBuilderFactory.get("step1Chunk")
.<Integer, Long>chunk(3)
.reader(reader)
.processor(processor)
.writer(writer)
.build();
}
The Reader i test in the step:
@Component
public class FirstItemReader implements ItemReader<Integer> {
@Autowired
DownloadService downloadService;
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
int index =0;
@Override
public Integer read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException {
System.out.println("in itemReader");
System.out.println(this.downloadService.downloadFile());
Integer item ;
if(index<list.size()){
item = list.get(index);
index++;
return item;
}
return null;
}
}
The extension Class:
public class MyExtension extends SpringExtension {
}
It's empty, but replacing it with an implementation which overrides the callback methods doesn't change anything, i get the same error.
The problem seems to be the combination with @MockBean. I want to add functionality to this MyExtension class, but i get the error.
The Full stacktrace:
org.springframework.beans.factory.BeanCreationException: Could not inject field: de.orgen.batchprocessing2.DownloadService de.orgen.batchprocessing2.chunkJobsTest.downloadService; nested exception is java.lang.IllegalStateException: The field de.orgen.batchprocessing2.DownloadService de.orgen.batchprocessing2.chunkJobsTest.downloadService cannot have an existing value
at org.springframework.boot.test.mock.mockito.MockitoPostProcessor.inject(MockitoPostProcessor.java:392)
at org.springframework.boot.test.mock.mockito.MockitoPostProcessor.inject(MockitoPostProcessor.java:380)
at org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener.lambda$injectFields$0(MockitoTestExecutionListener.java:80)
at org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener.postProcessFields(MockitoTestExecutionListener.java:104)
at org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener.injectFields(MockitoTestExecutionListener.java:79)
at org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener.prepareTestInstance(MockitoTestExecutionListener.java:54)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:246)
at org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:97)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$6(ClassBasedTestDescriptor.java:350)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.executeAndMaskThrowable(ClassBasedTestDescriptor.java:355)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$7(ClassBasedTestDescriptor.java:350)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175)
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1384)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
at java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:313)
at java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:743)
at java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:742)
at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:647)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeTestInstancePostProcessors(ClassBasedTestDescriptor.java:349)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$instantiateAndPostProcessTestInstance$4(ClassBasedTestDescriptor.java:270)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.instantiateAndPostProcessTestInstance(ClassBasedTestDescriptor.java:269)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$2(ClassBasedTestDescriptor.java:259)
at java.util.Optional.orElseGet(Optional.java:267)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$3(ClassBasedTestDescriptor.java:258)
at org.junit.jupiter.engine.execution.TestInstancesProvider.getTestInstances(TestInstancesProvider.java:31)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$prepare$0(TestMethodTestDescriptor.java:101)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:100)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:65)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$prepare$1(NodeTestTask.java:111)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.prepare(NodeTestTask.java:111)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:79)
at java.util.ArrayList.forEach(ArrayList.java:1259)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at java.util.ArrayList.forEach(ArrayList.java:1259)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:96)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:75)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:57)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)
Caused by: java.lang.IllegalStateException: The field de.orgen.batchprocessing2.DownloadService de.orgen.batchprocessing2.chunkJobsTest.downloadService cannot have an existing value
at org.springframework.util.Assert.state(Assert.java:94)
at org.springframework.boot.test.mock.mockito.MockitoPostProcessor.inject(MockitoPostProcessor.java:386)
... 71 more
I am using Spring Batch 4.1.2 and spring Boot 2.1.4.RELEASE, which i cannot change.
If you want to extend the testing facilities that involve Spring, you should not extend SpringExtension
but rather implement a TestExecutionListener
. This will more likely lead to what you want.
The SpringExtension
is currently added three times:
MyExtension
that you're explicitly registering,SpringExtension
as implicitly registered by @SpringJUnitConfig
,SpringExtension
by @SpringBootTest
.The latter two are effectively only one registration as it's the same class, but MyExtension
is considered to be different. The error you're seeing comes during the second execution after the variable downloadService
has already been set in the first execution.
The annotation @SpringJUnitConfig
has no functionality of its own but is meta-annotated with @ExtendWith(SpringExtension.class)
and @ContextConfiguration
. So to disable SpringExtension
, you can use @ContextConfiguration
directly. But with @SpringBootTest
you should actually be able to remove it completely.
Replacing @SpringBootTest
is more difficult.
You can try to override the method SpringExtension::postProcessTestInstance
in MyExtension
to do nothing, or in particular to not call TestContextManager::prepareTestInstance
. This may work in your particular case but may just lead to next problem down the road.