user11705123
user11705123

Reputation: 633

Spring-Batch: Testing custom itemReader

I am trying to test my custom itemReader :

@Bean
@StepScope
MyMultiLineItemReader itemReader(@Value("#{stepExecutionContext['fileName']}") String filename) throws MalformedURLException {
MyMultiLineItemReader itemReader = new MyMultiLineItemReader();
itemReader.setDelegate(myFlatFileItemReader(filename));

return itemReader;
}

@Bean
@StepScope
public FlatFileItemReader<String> myFlatFileItemReader(@Value("#{stepExecutionContext['fileName']}") String filename) throws MalformedURLException {
return new FlatFileItemReaderBuilder<String>()
.name("myFlatFileItemReader")
.resource(new UrlResource(filename))
.lineMapper(new PassThroughLineMapper())
.build();
}

my test class looks like

@Test
public void givenMockedStep_whenReaderCalled_thenSuccess() throws Exception {
    // given
    JobExecution jobExecution = new JobExecution(5l);
    ExecutionContext ctx = new ExecutionContext();
    ctx.put("fileName", "src/main/resources/data/input.txt");
    jobExecution.setExecutionContext(ctx);

    JobSynchronizationManager.register(jobExecution);
    StepExecution stepExecution = MetaDataInstanceFactory.createStepExecution(ctx);

    // when
    StepScopeTestUtils.doInStepScope(stepExecution, () -> {
     ...
     });
}

When I run the test case, the process fails because the fileName parameter is null.

I am looking for the right way of testing the itemReader.

Thanks

Upvotes: 4

Views: 7213

Answers (1)

Mahmoud Ben Hassine
Mahmoud Ben Hassine

Reputation: 31660

You don't need to create a JobExecution and register it in a JobSynchronizationManager to test a step-scoped component. Mocking a step execution and using it in StepScopeTestUtils.doInStepScope is enough. Here is a complete example:

import java.util.ArrayList;
import java.util.List;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder;
import org.springframework.batch.item.file.mapping.PassThroughLineMapper;
import org.springframework.batch.test.MetaDataInstanceFactory;
import org.springframework.batch.test.StepScopeTestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = StepScopedComponentTest.MyConfiguration.class)
public class StepScopedComponentTest {

    @Autowired
    private FlatFileItemReader<String> reader;

    @Test
    public void givenMockedStep_whenReaderCalled_thenSuccess() throws Exception {
        // given
        ExecutionContext ctx = new ExecutionContext();
        ctx.put("fileName", "data/input.txt");
        StepExecution stepExecution = MetaDataInstanceFactory.createStepExecution(ctx);

        // when
        List<String> items = StepScopeTestUtils.doInStepScope(stepExecution, () -> {
            List<String> result = new ArrayList<>();
            String item;
            reader.open(stepExecution.getExecutionContext());
            while ((item = reader.read()) != null) {
                result.add(item);
            }
            reader.close();
            return result;
        });
        
        // then
        Assert.assertEquals(2, items.size());
        Assert.assertEquals("foo", items.get(0));
        Assert.assertEquals("bar", items.get(1));
    }
    
    @Configuration
    @EnableBatchProcessing
    static class MyConfiguration {
        
        @Bean
        @StepScope
        public FlatFileItemReader<String> myFlatFileItemReader(@Value("#{stepExecutionContext['fileName']}") String filename) {
            return new FlatFileItemReaderBuilder<String>()
                    .name("myFlatFileItemReader")
                    .resource(new ClassPathResource(filename))
                    .lineMapper(new PassThroughLineMapper())
                    .build();
        }
    }
    
}

This test passes assuming the class path resource data/input.txt contains two lines foo and bar.

Upvotes: 6

Related Questions