简体   繁体   中英

@BeforeStep method is not called during unit test execution

I have an ItemProcessor which has a @BeforeStep method to access the ExecutionContext :

public class MegaProcessor implements ItemProcessor<String, String> {

    private ExecutionContext context;

    @BeforeStep
    void getExecutionContext(final StepExecution stepExecution) {
        this.context = stepExecution.getExecutionContext();
    }

    @Override
    public String process(final String string) throws Exception {
     // ...
    }
}

The unit test for this class:

@ContextConfiguration(classes = MegaProcessor.class)
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, StepScopeTestExecutionListener.class })
@RunWith(SpringRunner.class)
public class MegaProcessorTest {

    @Autowired
    private MegaProcessor sut;

    public StepExecution getStepExecution() {
        StepExecution execution = MetaDataInstanceFactory.createStepExecution();
        execution.getExecutionContext().put("data", "yeah");
        return execution;
    }

    @Test
    public void MegaProcessor() throws Exception {
        assertNotNull(sut.process("pew pew"));
    }
}

When I debug the test run, context is null and the @BeforeStep method is never called. Why is that and how to achieve that?

Why is that

If you want to use the StepScopeTestExecutionListener , the tested component should be step-scoped (See Javadoc ). It's not the case in your example. But this is not the real issue. The real issue is that the method annotated with @BeforeStep will be called before the step in which your processor is registered is executed. In your test case, there is no step running so the method is never called.

how to achieve that?

Since it is a unit test, you can assume the step execution will be passed to your item processor by Spring Batch before running the step and mock/stub it in your unit test. This is how I would unit test the component:

import org.junit.Before;
import org.junit.Test;

import org.springframework.batch.core.StepExecution;

import static org.junit.Assert.assertNotNull;

public class MegaProcessorTest {

    private MegaProcessor sut;

    @Before
    public void setUp() {
        StepExecution execution = MetaDataInstanceFactory.createStepExecution();
        execution.getExecutionContext().put("data", "yeah");
        sut = new MegaProcessor();
        sut.getExecutionContext(execution); // I would rename getExecutionContext to setExecutionContext
    }

    @Test
    public void MegaProcessor() throws Exception {
        assertNotNull(sut.process("pew pew"));
    }

}

The StepScopeTestExecutionListener is handy when you have step-scoped components that use late-binding to get values from the step execution context. For example:

@Bean
@StepScope
public ItemReader<String> itemReader(@Value("#{stepExecutionContext['items']}") String[] items) {
        return new ListItemReader<>(Arrays.asList(items));
}

A unit test of this reader would be something like:

import java.util.Arrays;

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.StepScope;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.support.ListItemReader;
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.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;

@ContextConfiguration(classes = StepScopeExecutionListenerSampleTest.MyApplicationContext.class)
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, StepScopeTestExecutionListener.class })
@RunWith(SpringRunner.class)
public class StepScopeExecutionListenerSampleTest {

    @Autowired
    private ItemReader<String> sut;

    public StepExecution getStepExecution() {
        StepExecution execution = MetaDataInstanceFactory.createStepExecution();
        execution.getExecutionContext().put("items", new String[] {"foo", "bar"});
        return execution;
    }

    @Test
    public void testItemReader() throws Exception {
        Assert.assertEquals("foo", sut.read());
        Assert.assertEquals("bar", sut.read());
        Assert.assertNull(sut.read());
    }

    @Configuration
    static class MyApplicationContext {

        @Bean
        @StepScope
        public ItemReader<String> itemReader(@Value("#{stepExecutionContext['items']}") String[] items) {
            return new ListItemReader<>(Arrays.asList(items));
        }

        /*
         * Either declare the step scope like the following or annotate the class
         * with `@EnableBatchProcessing` and the step scope will be added automatically
         */
        @Bean
        public static org.springframework.batch.core.scope.StepScope stepScope() {
            org.springframework.batch.core.scope.StepScope stepScope = new org.springframework.batch.core.scope.StepScope();
            stepScope.setAutoProxy(false);
            return stepScope;
        }
    }

}

Hope this helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM