我有一个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"));
}
}
当我调试测试运行时,context
是null
,并且从未调用@BeforeStep
方法。为什么会这样以及如何实现呢?
答案 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;
}
}
}
希望这会有所帮助。