1

I have a Chunk Tasklet with a reader and writer, and I am trying to make a Unit Test for it, but I face multiples issues.

I used the answer of the question below, to help creating my Unit Test:
How define a spring batch chunk in the tasklet with code
Its seems to be the closest to, what I want to make.

@ContextConfiguration(locations = { "classpath:config/beans-unittest-service.xml",
        "classpath:config/beans-unittest-item.xml" })
@RunWith(SpringJUnit4ClassRunner.class)
public class KpiMetricFromCsvFileTaskletTest extends RegistryServiceImplTest {

    @Autowired
    @Qualifier("kpiMetricCsvItemReader")
    ItemReader<KpiMetric> reader;

    @Autowired
    @Qualifier("kpiMetricCreatorWriter")
    KpiMetricCreatorWriter writer;

    @Override
    @Before
    public void init() {
        writer.setRegistryService(registryService);
        writer.setRegistry(registry);
        setCreateKpiMeasureBehaviour();
    }

    private ChunkContext createChunkContext() {
        StepExecution stepExecution = Mockito.mock(StepExecution.class);
        StepContext stepContext = Mockito.mock(StepContext.class);
        ChunkContext chunkContext = Mockito.mock(ChunkContext.class);
        JobExecution jobExecution = MetaDataInstanceFactory.createJobExecution();

        Mockito.when(chunkContext.getStepContext()).thenReturn(stepContext);
        Mockito.when(stepContext.getStepExecution()).thenReturn(stepExecution);
        Mockito.when(stepExecution.getJobExecution()).thenReturn(jobExecution);

        return chunkContext;
    }

    @Test
    public void testTasklet() throws Exception {
        StepContribution contribution = Mockito.mock(StepContribution.class);
        ChunkContext chunkContext = createChunkContext();

        Tasklet tasklet = new Tasklet() {

            @Override
            public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
                KpiMetric kpi = reader.read();
                List<KpiMetric> items = new ArrayList<>();
                while (kpi != null) {
                    items.add(kpi);
                }
                writer.write(items);
                return RepeatStatus.FINISHED;
            }
        };
        tasklet.execute(contribution, chunkContext);
    }
}

  • My Unit Test Class extends another Class, that contains methods, that will set the mock object behaviour.
  • I have two configuration files. One of them contains a reader bean kpiMetricCsvItemReader and a writer bean kpiMetricCreatorWriter.
  • In my Unit Test init method, I change the service attribute of my writer to a mock service object, I also mock the context objects, just like in the answer of the link above.

The problem is that, in the testTasklet method, I want to create a Tasklet and execute it, but when I run the program, I get the error:

org.springframework.batch.item.ReaderNotOpenException: Reader must be open before it can be read.

I understand that the file is not opened, but when I launch my Job with Spring Batch, I don't get that error, so Spring should be opening my file by itself.

Now to solve this, I should either manage to open the file for the reader or delegate the creation of the tasklet to something else, that will handle it, but how?


EDIT

Perhaps showing the reader configuration file should help :

    <bean id="kpiMetricCsvItemReader" class="org.springframework.batch.item.file.FlatFileItemReader">
        <property name="resource" value="classpath:${kpiMetricCsvFile}" />
        <property name="linesToSkip" value="1"/>
        <property name="lineMapper">
            <bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
                <property name="lineTokenizer">
                    <bean class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
                        <property name="delimiter" value=";"/>
                        <property name="names" value="key,description,name,score,tool,variable_type" />
                    </bean>
                </property>
                <property name="fieldSetMapper">
                    <bean class="com.example.fieldmapper.KpiMetricFieldSetMapper" />
                </property>
            </bean>
        </property>
    </bean>

    <bean id="kpiMetricCreatorWriter" class="com.example.writer.kpi.KpiMetricCreatorWriter" >
        <property name="registry" ref="registry" />
        <property name="registryService" ref="registryService" />
    </bean>
Hamza Ince
  • 604
  • 17
  • 46

1 Answers1

1

When used outside a chunk-oriented step, an item reader/writer should be opened/closed manually. So in your case, you need to open the reader yourself before using it to read data.

You can refer to the unit test of the FlatFileItemReader itself as an example.

Mahmoud Ben Hassine
  • 28,519
  • 3
  • 32
  • 50
  • Ok, I get my problem, I use the ItemWriter interface, but my writer is actually a FlatFileItemReader as you can see in the spring configuration file, I will just change it in my Class. I'll see if its work tomorrow, (it probably will) thanks for the help. – Hamza Ince Aug 29 '19 at 19:24
  • I made a mistake, in my previous comment, I meant ItemReader instead of ItemWriter and reader instead of writer – Hamza Ince Aug 30 '19 at 07:42