单元测试
与其他应用程序样式一样,对编写的任何代码进行单元测试非常重要
作为批处理作业的一部分。Spring 核心文档介绍了如何进行单元和集成
test 与 Spring 一起进行非常详细的测试,因此这里不再重复。然而,重要的是,
考虑如何 “端到端” 测试 Batch 作业,这就是本章介绍的内容。
该项目包括有助于此端到端测试的类
方法。spring-batch-test
创建 Unit Test 类
要使单元测试运行批处理作业,框架必须加载作业的 .两个注释用于触发此行为:ApplicationContext
-
@SpringJUnitConfig
指示该类应使用 Spring 的 JUnit 工具 -
@SpringBatchTest
在测试上下文中注入Spring Batch测试实用程序(例如 和 )JobLauncherTestUtils
JobRepositoryTestUtils
如果测试上下文包含单个 bean 定义,则此
bean 将在 中自动连接。否则,作业
Under test 应在 上手动设置 。Job JobLauncherTestUtils 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 { ... }
批处理作业的端到端测试
“端到端”测试可以定义为从以下位置测试批处理作业的完整运行 从头到尾。这允许设置测试条件、执行作业、 并验证最终结果。
考虑一个从数据库中读取并写入平面文件的批处理作业示例。
测试方法首先使用测试数据设置数据库。它将清除表,然后插入 10 条新记录。然后,测试使用 该方法启动 by。该方法由类提供。该类还提供了方法,该方法允许测试提供特定参数。方法
返回对象,这对于断言特定信息很有用
关于跑步。在以下情况下,测试验证以
状态为 .CUSTOMER
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());
}
}
测试各个步骤
对于复杂的批处理作业,端到端测试方法中的测试用例可能会变为
不可收拾。在这些情况下,使用测试用例来测试个人可能更有用
他们自己走。该类包含一个名为
它采用步骤名称并仅运行该特定 .这种方法允许
更有针对性的测试:让测试仅为该步骤设置数据并验证其
结果。以下示例演示如何使用该方法加载 by name:JobLauncherTestUtils
launchStep
Step
launchStep
Step
JobExecution jobExecution = jobLauncherTestUtils.launchStep("loadFileStep");
测试 Step-Scoped 组件
通常,在运行时为步骤配置的组件使用 step scope 和
late 绑定,用于从步骤或作业执行中注入上下文。这些很难测试,因为
独立组件,除非你有办法将上下文设置为一个步骤中
执行。这是 Spring Batch 中两个组件的目标:和 。StepScopeTestExecutionListener
StepScopeTestUtils
侦听器在类级别声明,其工作是创建步骤执行 context 中,如下例所示:
@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());
}
}
有两个 .一个是常规的 Spring Test 框架,它
处理来自已配置应用程序上下文的依赖项注入以注入读取器。
另一个是 Spring Batch 。它的工作原理是查找
factory 方法,将其用作
test 方法,就好像该执行在 at runtime 中处于活动状态一样。工厂方法
通过其签名检测到(它必须返回 a )。如果工厂方法是
not provided,则创建一个 default。TestExecutionListeners
StepScopeTestExecutionListener
StepExecution
Step
StepExecution
StepExecution
从 v4.1 开始,和 将作为测试执行侦听器导入
如果测试类带有 .前面的测试
example 可以按如下方式配置: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());
}
}
如果您希望步骤范围的持续时间为
执行测试方法。对于更灵活但更具侵入性的方法,您可以使用
这。以下示例计算
上一个示例中显示的 Reader: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 提供了一个称为的类,以方便验证输出文件。调用的方法采用
两个对象(或两个对象)并逐行断言
文件具有相同的内容。因此,可以创建具有预期
output 并将其与实际结果进行比较,如下例所示:AssertFile
assertFileEquals
File
Resource
private static final String EXPECTED_FILE = "src/main/resources/data/input.txt";
private static final String OUTPUT_FILE = "target/test-outputs/output.txt";
AssertFile.assertFileEquals(new FileSystemResource(EXPECTED_FILE),
new FileSystemResource(OUTPUT_FILE));
模拟域对象
为 Spring Batch 编写单元和集成测试时遇到的另一个常见问题
components 是如何模拟域对象。一个很好的例子是 ,因为
以下代码片段显示:StepExecutionListener
public class NoWorkFoundStepExecutionListener extends StepExecutionListenerSupport {
public ExitStatus afterStep(StepExecution stepExecution) {
if (stepExecution.getReadCount() == 0) {
return ExitStatus.FAILED;
}
return null;
}
}
框架提供了前面的侦听器示例,并检查 a 是否为空读取计数,从而表示未完成任何工作。虽然此示例是
相当简单,它用于说明您在
您尝试对实现需要 Spring Batch 域的接口的类进行单元测试
对象。请考虑对前面示例中的侦听器进行以下单元测试:StepExecution
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 域模型遵循良好的面向对象原则,因此需要一个 ,而 则需要一个 和 ,才能创建有效的 .虽然这在坚实的领域中是好的
模型,它确实使创建用于单元测试的存根对象变得冗长。为了解决这个问题,
Spring Batch 测试模块包括一个用于创建域对象的工厂:。给定此工厂,可以将单元测试更新为更多
简洁,如下例所示:StepExecution
JobExecution
JobInstance
JobParameters
StepExecution
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());
}
上述创建 simple 的方法只是一种便捷的方法
在工厂内可用。您可以在其 Javadoc 中找到完整的方法列表。StepExecution