单元测试
与其他应用程序样式一样,对编写的任何代码进行单元测试非常重要
作为批处理作业的一部分。Spring 核心文档涵盖了如何单元化和集成
用 Spring 进行非常详细的测试,所以这里不再重复。然而,重要的是,
思考如何“端到端”测试批处理作业,这就是本章所介绍的内容。
这spring-batch-test
项目包括促进此端到端测试的类
方法。
创建单元测试类
要使单元测试运行批处理作业,框架必须加载作业的ApplicationContext
.两个注释用于触发此行为:
-
@SpringJUnitConfig
表示类应该使用 Spring 的 JUnit 设施 -
@SpringBatchTest
注入 Spring Batch 测试实用程序(例如JobLauncherTestUtils
和JobRepositoryTestUtils
) 在测试上下文中
如果测试上下文包含单个Job bean 定义,这个
bean 将在JobLauncherTestUtils .否则,作业
under test 应在JobLauncherTestUtils . |
-
Java
-
XML
以下 Java 示例显示了正在使用的注释:
@SpringBatchTest
@SpringJUnitConfig(SkipSampleConfiguration.class)
public class SkipSampleFunctionalTests { ... }
以下 XML 示例显示了正在使用的注释:
@SpringBatchTest
@SpringJUnitConfig(locations = { "/simple-job-launcher-context.xml",
"/jobs/skipSampleJob.xml" })
public class SkipSampleFunctionalTests { ... }
批处理作业的端到端测试
“端到端”测试可以定义为测试批处理作业的完整运行 从头到尾。这允许设置测试条件、执行作业、 并验证最终结果。
考虑一个从数据库读取并写入平面文件的批处理作业示例。
测试方法首先使用测试数据设置数据库。它清除了CUSTOMER
表,然后插入 10 条新记录。然后,测试会启动Job
通过使用launchJob()
方法。这launchJob()
方法由JobLauncherTestUtils
类。这JobLauncherTestUtils
类还提供了launchJob(JobParameters)
方法,它允许测试给出特定参数。这launchJob()
方法
返回JobExecution
对象,这对于断言特定信息很有用
关于Job
跑。在以下情况下,测试会验证Job
结束为
状态为COMPLETED
.
-
Java
-
XML
以下列表显示了 Java 配置样式的 JUnit 5 示例:
@SpringBatchTest
@SpringJUnitConfig(SkipSampleConfiguration.class)
public class SkipSampleFunctionalTests {
@Autowired
private JobLauncherTestUtils jobLauncherTestUtils;
private JdbcTemplate jdbcTemplate;
@Autowired
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
@Test
public void testJob(@Autowired Job job) throws Exception {
this.jobLauncherTestUtils.setJob(job);
this.jdbcTemplate.update("delete from CUSTOMER");
for (int i = 1; i <= 10; i++) {
this.jdbcTemplate.update("insert into CUSTOMER values (?, 0, ?, 100000)",
i, "customer" + i);
}
JobExecution jobExecution = jobLauncherTestUtils.launchJob();
Assert.assertEquals("COMPLETED", jobExecution.getExitStatus().getExitCode());
}
}
以下列表显示了 XML 配置样式的 JUnit 5 示例:
@SpringBatchTest
@SpringJUnitConfig(locations = { "/simple-job-launcher-context.xml",
"/jobs/skipSampleJob.xml" })
public class SkipSampleFunctionalTests {
@Autowired
private JobLauncherTestUtils jobLauncherTestUtils;
private JdbcTemplate jdbcTemplate;
@Autowired
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
@Test
public void testJob(@Autowired Job job) throws Exception {
this.jobLauncherTestUtils.setJob(job);
this.jdbcTemplate.update("delete from CUSTOMER");
for (int i = 1; i <= 10; i++) {
this.jdbcTemplate.update("insert into CUSTOMER values (?, 0, ?, 100000)",
i, "customer" + i);
}
JobExecution jobExecution = jobLauncherTestUtils.launchJob();
Assert.assertEquals("COMPLETED", jobExecution.getExitStatus().getExitCode());
}
}
测试单个步骤
对于复杂的批处理作业,端到端测试方法中的测试用例可能会变成
不可收拾。在这些情况下,使用测试用例来测试单个
自己STOMP踏。这JobLauncherTestUtils
class 包含一个名为launchStep
,
它采用一个步骤名称并仅运行该特定Step
.这种方法允许
更有针对性的测试,让测试仅为该步骤设置数据并验证其
结果直接。以下示例演示如何使用launchStep
加载Step
按名称:
JobExecution jobExecution = jobLauncherTestUtils.launchStep("loadFileStep");
测试步进范围组件
通常,在运行时为步骤配置的组件使用步骤作用域和
延迟绑定,用于从步骤或作业执行中注入上下文。这些很难测试,因为
独立组件,除非你有办法设置上下文,就好像它们在步骤中一样
执行。这是 Spring Batch 中两个组件的目标:StepScopeTestExecutionListener
和StepScopeTestUtils
.
监听器在类级别声明,它的工作是为每个测试方法创建一个步骤执行上下文,如以下示例所示:
@SpringJUnitConfig
@TestExecutionListeners( { DependencyInjectionTestExecutionListener.class,
StepScopeTestExecutionListener.class })
public class StepScopeTestExecutionListenerIntegrationTests {
// This component is defined step-scoped, so it cannot be injected unless
// a step is active...
@Autowired
private ItemReader<String> reader;
public StepExecution getStepExecution() {
StepExecution execution = MetaDataInstanceFactory.createStepExecution();
execution.getExecutionContext().putString("input.data", "foo,bar,spam");
return execution;
}
@Test
public void testReader() {
// The reader is initialized and bound to the input data
assertNotNull(reader.read());
}
}
有两个TestExecutionListeners
. 一个是常规的 Spring Test 框架,它处理来自配置的应用程序上下文的依赖注入以注入读取器。另一个是 Spring BatchStepScopeTestExecutionListener
. 它的工作原理是在测试用例中查找factory 方法,以StepExecution
,将其用作test 方法的上下文,就好像该执行在Step
在运行时。工厂方法由其签名检测(它必须返回StepExecution
). 如果工厂方法是not provided ,则默认值为StepExecution
被创建。
从 v4.1 开始,StepScopeTestExecutionListener
和JobScopeTestExecutionListener
作为测试执行监听器导入如果测试类被@SpringBatchTest
. 前面的测试示例可以配置如下:
@SpringBatchTest
@SpringJUnitConfig
public class StepScopeTestExecutionListenerIntegrationTests {
// This component is defined step-scoped, so it cannot be injected unless
// a step is active...
@Autowired
private ItemReader<String> reader;
public StepExecution getStepExecution() {
StepExecution execution = MetaDataInstanceFactory.createStepExecution();
execution.getExecutionContext().putString("input.data", "foo,bar,spam");
return execution;
}
@Test
public void testReader() {
// The reader is initialized and bound to the input data
assertNotNull(reader.read());
}
}
如果您希望步骤作用域的持续时间为执行测试方法。对于更灵活但更具侵入性的方法,您可以使用 这StepScopeTestUtils
. 以下示例计算了上一个示例中显示的阅读器中可用的项目数:
int count = StepScopeTestUtils.doInStepScope(stepExecution,
new Callable<Integer>() {
public Integer call() throws Exception {
int count = 0;
while (reader.read() != null) {
count++;
}
return count;
}
});
模拟域对象
在为 Spring Batch 编写单元和集成测试时遇到的另一个常见问题组件是如何模拟域对象。一个很好的例子是StepExecutionListener
如 以下代码片段显示:
public class NoWorkFoundStepExecutionListener implements StepExecutionListener {
public ExitStatus afterStep(StepExecution stepExecution) {
if (stepExecution.getReadCount() == 0) {
return ExitStatus.FAILED;
}
return null;
}
}
该框架提供了前面的侦听器示例,并检查了StepExecution
对于空读取计数,因此表示没有完成任何工作。虽然这个例子相当简单,但它用来说明你可能遇到的问题类型你尝试单元测试实现需要 Spring Batch 域的接口的类 对象。 请考虑在前面示例中对侦听器的以下单元测试:
private NoWorkFoundStepExecutionListener tested = new NoWorkFoundStepExecutionListener();
@Test
public void noWork() {
StepExecution stepExecution = new StepExecution("NoProcessingStep",
new JobExecution(new JobInstance(1L, new JobParameters(),
"NoProcessingJob")));
stepExecution.setExitStatus(ExitStatus.COMPLETED);
stepExecution.setReadCount(0);
ExitStatus exitStatus = tested.afterStep(stepExecution);
assertEquals(ExitStatus.FAILED.getExitCode(), exitStatus.getExitCode());
}
由于 Spring Batch 域模型遵循良好的面向对象原则,因此StepExecution
需要一个JobExecution
,这需要一个JobInstance
和JobParameters
,以创建有效的StepExecution
. 虽然这在实体域模型中很好,但它确实使创建用于单元测试的存根对象变得冗长。为了解决这个问题,Spring Batch 测试模块包括一个用于创建域对象的工厂:MetaDataInstanceFactory
. 鉴于此工厂,可以将单元测试更新为更多简洁,如以下示例所示:
private NoWorkFoundStepExecutionListener tested = new NoWorkFoundStepExecutionListener();
@Test
public void testAfterStep() {
StepExecution stepExecution = MetaDataInstanceFactory.createStepExecution();
stepExecution.setExitStatus(ExitStatus.COMPLETED);
stepExecution.setReadCount(0);
ExitStatus exitStatus = tested.afterStep(stepExecution);
assertEquals(ExitStatus.FAILED.getExitCode(), exitStatus.getExitCode());
}
前面创建简单StepExecution
只是一种方便的方法在工厂内可用。您可以在其 Javadoc 中找到完整的方法列表。