Search code examples
springspring-bootspring-batchjunit5

BeanCreationException: Could not inject field:cannot have an existing value


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.


Solution

  • Short answer

    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.

    Long answer

    The SpringExtension is currently added three times:

    • once in the form your MyExtension that you're explicitly registering,
    • once as SpringExtension as implicitly registered by @SpringJUnitConfig,
    • and once more as 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.