在单元测试执行期间未调用@BeforeStep方法

时间:2018-09-05 17:26:54

标签: java spring-batch spring-boot-test

我有一个ItemProcessor,它有一个@BeforeStep方法来访问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 {
     // ...
    }
}

此类的单元测试:

@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"));
    }
}

当我调试测试运行时,contextnull,并且从未调用@BeforeStep方法。为什么会这样以及如何实现呢?

1 个答案:

答案 0 :(得分:1)

  

那是为什么

如果要使用StepScopeTestExecutionListener,则应对测试的组件进行步进作用域检查(请参见Javadoc)。在您的示例中情况并非如此。但这不是真正的问题。真正的问题是,在执行处理器注册步骤之前,将调用用@BeforeStep注释的方法。在您的测试用例中,没有任何步骤正在运行,因此永远不会调用该方法。

  

如何实现?

由于它是一个单元测试,因此您可以假定在运行步骤并将其模拟/存入单元测试之前,Spring Batch将把步骤执行传递给项目处理器。这就是我对组件进行单元测试的方式:

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"));
    }

}

当您具有使用后绑定从步骤执行上下文中获取值的步进作用域组件时,StepScopeTestExecutionListener会很方便。例如:

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

该阅读器的单元测试将类似于:

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;
        }
    }

}

希望这会有所帮助。