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

6tqwzwtp  于 2023-04-04  发布在  Java
关注(0)|答案(1)|浏览(190)

我有一个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方法从未被调用。为什么会这样?如何实现?

ttcibm8c

ttcibm8c1#

为什么会这样
如果你想使用StepScopeTestExecutionListener,测试的组件应该是step-scoped(参见Javadoc)。在你的例子中不是这样的。但这不是真实的的问题。真正的问题是,在执行你的处理器注册的步骤之前,用@BeforeStep注解的方法将被调用。在你的测试用例中,没有步骤运行,所以方法永远不会被调用。
如何做到这一点?
由于它是一个单元测试,你可以假设步骤执行将在运行步骤之前通过Spring Batch传递给你的item processor,并在你的单元测试中模拟/存根它。这就是我如何对组件进行单元测试:

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

}

当你有一个step-scoped组件,使用后期绑定从步骤执行上下文获取值时,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;
        }
    }

}

希望这个有用。

相关问题