【问题标题】:@BeforeStep method is not called during unit test execution单元测试执行期间不调用@BeforeStep 方法
【发布时间】:2019-02-10 22:03:48
【问题描述】:

我有一个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 方法。为什么会这样以及如何实现?

【问题讨论】:

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


    【解决方案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;
            }
        }
    
    }
    

    希望这会有所帮助。

    【讨论】:

    • 这是最简洁的简单示例(即不依赖于大量依赖项)春季批处理测试。这应该进入春季批量测试文档!来自 Spring Batch 的当前文档 [Spring 4.1 测试文档站点] (docs.spring.io/spring-batch/4.1.x/reference/html/…) 很难理解,并且没有说明正在测试什么。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多