This commit updates URLs to prefer the https protocol. Redirects are not followed to avoid accidentally expanding intentionally shortened URLs (i.e. if using a URL shortener). # HTTP URLs that Could Not Be Fixed These URLs were unable to be fixed. Please review them to see if they can be manually resolved. * [ ] http://erik.eae.net/archives/2007/07/27/18.54.15/ (200) with 1 occurrences could not be migrated: ([https](https://erik.eae.net/archives/2007/07/27/18.54.15/) result SSLHandshakeException). * [ ] http://gregfranko.com/jquery.tocify.js/ (200) with 1 occurrences could not be migrated: ([https](https://gregfranko.com/jquery.tocify.js/) result SSLHandshakeException). * [ ] http://javascript.nwbox.com/IEContentLoaded/ (200) with 1 occurrences could not be migrated: ([https](https://javascript.nwbox.com/IEContentLoaded/) result SSLHandshakeException). * [ ] http://jeremyworboys.com/writing/toggle-buttons-without-javascript (200) with 1 occurrences could not be migrated: ([https](https://jeremyworboys.com/writing/toggle-buttons-without-javascript) result SSLHandshakeException). * [ ] http://nicolasgallagher.com/micro-clearfix-hack/ (200) with 1 occurrences could not be migrated: ([https](https://nicolasgallagher.com/micro-clearfix-hack/) result SSLHandshakeException). * [ ] http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html (200) with 1 occurrences could not be migrated: ([https](https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html) result SSLException). * [ ] http://blindsignals.com/index.php/2009/07/jquery-delay/ (301) with 1 occurrences could not be migrated: ([https](https://blindsignals.com/index.php/2009/07/jquery-delay/) result SSLHandshakeException). # Fixed URLs ## Fixed But Review Recommended These URLs were fixed, but the https status was not OK. However, the https status was the same as the http request or http redirected to an https URL, so they were migrated. Your review is recommended. * [ ] http://jsperf.com/getall-vs-sizzle/2 (301) with 1 occurrences migrated to: https://jsperf.com/getall-vs-sizzle/2 ([https](https://jsperf.com/getall-vs-sizzle/2) result ReadTimeoutException). * [ ] http://wiki.ecmascript.org/doku.php?id=harmony:egal (ConnectTimeoutException) with 1 occurrences migrated to: https://wiki.ecmascript.org/doku.php?id=harmony:egal ([https](https://wiki.ecmascript.org/doku.php?id=harmony:egal) result ConnectTimeoutException). * [ ] http://www.w3.org/Addressing/URL (ReadTimeoutException) with 1 occurrences migrated to: https://www.w3.org/Addressing/URL ([https](https://www.w3.org/Addressing/URL) result SSLException). * [ ] http://api.example.com (UnknownHostException) with 1 occurrences migrated to: https://api.example.com ([https](https://api.example.com) result UnknownHostException). * [ ] http://jira.codehaus.org/browse/WSTX-165 (UnknownHostException) with 1 occurrences migrated to: https://jira.codehaus.org/browse/WSTX-165 ([https](https://jira.codehaus.org/browse/WSTX-165) result UnknownHostException). * [ ] http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ (301) with 1 occurrences migrated to: https://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ ([https](https://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/) result 404). * [ ] http://help.github.com/send-pull-requests (404) with 1 occurrences migrated to: https://help.github.com/send-pull-requests ([https](https://help.github.com/send-pull-requests) result 404). * [ ] http://json.org/json2.js (404) with 1 occurrences migrated to: https://json.org/json2.js ([https](https://json.org/json2.js) result 404). ## Fixed Success These URLs were switched to an https URL with a 2xx status. While the status was successful, your review is still recommended. * [ ] http://backbonejs.org with 1 occurrences migrated to: https://backbonejs.org ([https](https://backbonejs.org) result 200). * [ ] http://bugs.jquery.com/ticket/12282 with 1 occurrences migrated to: https://bugs.jquery.com/ticket/12282 ([https](https://bugs.jquery.com/ticket/12282) result 200). * [ ] http://bugs.jquery.com/ticket/12359 with 1 occurrences migrated to: https://bugs.jquery.com/ticket/12359 ([https](https://bugs.jquery.com/ticket/12359) result 200). * [ ] http://bugs.jquery.com/ticket/13378 with 1 occurrences migrated to: https://bugs.jquery.com/ticket/13378 ([https](https://bugs.jquery.com/ticket/13378) result 200). * [ ] http://db.apache.org/derby/papers/versionupgrade.html with 1 occurrences migrated to: https://db.apache.org/derby/papers/versionupgrade.html ([https](https://db.apache.org/derby/papers/versionupgrade.html) result 200). * [ ] http://diveintohtml5.info/history.html with 1 occurrences migrated to: https://diveintohtml5.info/history.html ([https](https://diveintohtml5.info/history.html) result 200). * [ ] http://docs.oracle.com/javase/tutorial/java/generics/capture.html with 2 occurrences migrated to: https://docs.oracle.com/javase/tutorial/java/generics/capture.html ([https](https://docs.oracle.com/javase/tutorial/java/generics/capture.html) result 200). * [ ] http://github.com/spring-projects/spring-batch with 1 occurrences migrated to: https://github.com/spring-projects/spring-batch ([https](https://github.com/spring-projects/spring-batch) result 200). * [ ] http://jira.spring.io/browse/BATCH-1681 with 1 occurrences migrated to: https://jira.spring.io/browse/BATCH-1681 ([https](https://jira.spring.io/browse/BATCH-1681) result 200). * [ ] http://jira.spring.io/browse/BATCH-761 with 1 occurrences migrated to: https://jira.spring.io/browse/BATCH-761 ([https](https://jira.spring.io/browse/BATCH-761) result 200). * [ ] http://jquery.com/ with 1 occurrences migrated to: https://jquery.com/ ([https](https://jquery.com/) result 200). * [ ] http://projects.spring.io/spring-batch/ with 2 occurrences migrated to: https://projects.spring.io/spring-batch/ ([https](https://projects.spring.io/spring-batch/) result 200). * [ ] http://projects.spring.io/spring-batch/faq.html with 1 occurrences migrated to: https://projects.spring.io/spring-batch/faq.html ([https](https://projects.spring.io/spring-batch/faq.html) result 200). * [ ] http://projects.spring.io/spring-integration/ with 1 occurrences migrated to: https://projects.spring.io/spring-integration/ ([https](https://projects.spring.io/spring-integration/) result 200). * [ ] http://sizzlejs.com/ with 2 occurrences migrated to: https://sizzlejs.com/ ([https](https://sizzlejs.com/) result 200). * [ ] http://spring.io/guides/gs/batch-processing/ with 1 occurrences migrated to: https://spring.io/guides/gs/batch-processing/ ([https](https://spring.io/guides/gs/batch-processing/) result 200). * [ ] http://spring.io/projects with 1 occurrences migrated to: https://spring.io/projects ([https](https://spring.io/projects) result 200). * [ ] http://spring.io/tools with 1 occurrences migrated to: https://spring.io/tools ([https](https://spring.io/tools) result 200). * [ ] http://spring.io/tools/sts/ with 1 occurrences migrated to: https://spring.io/tools/sts/ ([https](https://spring.io/tools/sts/) result 200). * [ ] http://stackoverflow.com/a/20002493/2910265 with 1 occurrences migrated to: https://stackoverflow.com/a/20002493/2910265 ([https](https://stackoverflow.com/a/20002493/2910265) result 200). * [ ] http://stackoverflow.com/questions/134882/undoing-a-git-rebase with 1 occurrences migrated to: https://stackoverflow.com/questions/134882/undoing-a-git-rebase ([https](https://stackoverflow.com/questions/134882/undoing-a-git-rebase) result 200). * [ ] http://underscorejs.org with 1 occurrences migrated to: https://underscorejs.org ([https](https://underscorejs.org) result 200). * [ ] http://unlicense.org with 1 occurrences migrated to: https://unlicense.org ([https](https://unlicense.org) result 200). * [ ] http://www.apache.org with 1 occurrences migrated to: https://www.apache.org ([https](https://www.apache.org) result 200). * [ ] http://xmlns.jcp.org/xml/ns/javaee/jobXML_1_0.xsd (301) with 1 occurrences migrated to: https://www.oracle.com/webfolder/technetwork/jsc/xml/ns/javaee/jobXML_1_0.xsd ([https](https://xmlns.jcp.org/xml/ns/javaee/jobXML_1_0.xsd) result 200). * [ ] http://www.ow2.org/ with 1 occurrences migrated to: https://www.ow2.org/ ([https](https://www.ow2.org/) result 200). * [ ] http://www.springframework.org/schema/beans/spring-beans.xsd with 3 occurrences migrated to: https://www.springframework.org/schema/beans/spring-beans.xsd ([https](https://www.springframework.org/schema/beans/spring-beans.xsd) result 200). * [ ] http://www.springframework.org/schema/integration/spring-integration.xsd with 3 occurrences migrated to: https://www.springframework.org/schema/integration/spring-integration.xsd ([https](https://www.springframework.org/schema/integration/spring-integration.xsd) result 200). * [ ] http://www.w3.org/TR/2011/REC-css3-selectors-20110929/ with 2 occurrences migrated to: https://www.w3.org/TR/2011/REC-css3-selectors-20110929/ ([https](https://www.w3.org/TR/2011/REC-css3-selectors-20110929/) result 200). * [ ] http://www.w3.org/TR/CSS21/syndata.html with 2 occurrences migrated to: https://www.w3.org/TR/CSS21/syndata.html ([https](https://www.w3.org/TR/CSS21/syndata.html) result 200). * [ ] http://www.w3.org/TR/selectors/ with 4 occurrences migrated to: https://www.w3.org/TR/selectors/ ([https](https://www.w3.org/TR/selectors/) result 200). * [ ] http://contributor-covenant.org with 1 occurrences migrated to: https://contributor-covenant.org ([https](https://contributor-covenant.org) result 301). * [ ] http://contributor-covenant.org/version/1/3/0/ with 1 occurrences migrated to: https://contributor-covenant.org/version/1/3/0/ ([https](https://contributor-covenant.org/version/1/3/0/) result 301). * [ ] http://dev.w3.org/csswg/cssom/ with 1 occurrences migrated to: https://dev.w3.org/csswg/cssom/ ([https](https://dev.w3.org/csswg/cssom/) result 301). * [ ] http://docs.python.org/library/functions.html with 1 occurrences migrated to: https://docs.python.org/library/functions.html ([https](https://docs.python.org/library/functions.html) result 301). * [ ] http://docs.spring.io/spring-batch/ with 1 occurrences migrated to: https://docs.spring.io/spring-batch/ ([https](https://docs.spring.io/spring-batch/) result 301). * [ ] http://docs.spring.io/spring-batch/apidocs/org/springframework/batch/core/launch/JobOperator.html with 1 occurrences migrated to: https://docs.spring.io/spring-batch/apidocs/org/springframework/batch/core/launch/JobOperator.html ([https](https://docs.spring.io/spring-batch/apidocs/org/springframework/batch/core/launch/JobOperator.html) result 301). * [ ] http://docs.spring.io/spring-batch/apidocs/org/springframework/batch/test/MetaDataInstanceFactory.html with 1 occurrences migrated to: https://docs.spring.io/spring-batch/apidocs/org/springframework/batch/test/MetaDataInstanceFactory.html ([https](https://docs.spring.io/spring-batch/apidocs/org/springframework/batch/test/MetaDataInstanceFactory.html) result 301). * [ ] http://docs.spring.io/spring-batch/reference/html/ with 1 occurrences migrated to: https://docs.spring.io/spring-batch/reference/html/ ([https](https://docs.spring.io/spring-batch/reference/html/) result 301). * [ ] http://docs.spring.io/spring-batch/reference/html/scalability.html with 1 occurrences migrated to: https://docs.spring.io/spring-batch/reference/html/scalability.html ([https](https://docs.spring.io/spring-batch/reference/html/scalability.html) result 301). * [ ] http://docs.spring.io/spring-batch/trunk/reference/ with 1 occurrences migrated to: https://docs.spring.io/spring-batch/trunk/reference/ ([https](https://docs.spring.io/spring-batch/trunk/reference/) result 301). * [ ] http://docs.spring.io/spring-batch/trunk/reference/html/index.html with 1 occurrences migrated to: https://docs.spring.io/spring-batch/trunk/reference/html/index.html ([https](https://docs.spring.io/spring-batch/trunk/reference/html/index.html) result 301). * [ ] http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/transaction/interceptor/TransactionAttributeEditor.html with 1 occurrences migrated to: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/transaction/interceptor/TransactionAttributeEditor.html ([https](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/transaction/interceptor/TransactionAttributeEditor.html) result 301). * [ ] http://forum.spring.io/forum/spring-projects/batch with 2 occurrences migrated to: https://forum.spring.io/forum/spring-projects/batch ([https](https://forum.spring.io/forum/spring-projects/batch) result 301). * [ ] http://help.github.com/fork-a-repo/ with 2 occurrences migrated to: https://help.github.com/fork-a-repo/ ([https](https://help.github.com/fork-a-repo/) result 301). * [ ] http://help.github.com/forking/ with 1 occurrences migrated to: https://help.github.com/forking/ ([https](https://help.github.com/forking/) result 301). * [ ] http://help.github.com/send-pull-requests/ with 2 occurrences migrated to: https://help.github.com/send-pull-requests/ ([https](https://help.github.com/send-pull-requests/) result 301). * [ ] http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_ with 1 occurrences migrated to: https://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_ ([https](https://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_) result 301). * [ ] http://jquery.org/license with 2 occurrences migrated to: https://jquery.org/license ([https](https://jquery.org/license) result 301). * [ ] http://springframework.org/batch/sample/io/oxm/domain with 3 occurrences migrated to: https://springframework.org/batch/sample/io/oxm/domain ([https](https://springframework.org/batch/sample/io/oxm/domain) result 301). * [ ] http://www.eaipatterns.com/toc.html (302) with 1 occurrences migrated to: https://www.enterpriseintegrationpatterns.com/toc.html ([https](https://www.eaipatterns.com/toc.html) result 301). * [ ] http://www.spring.io with 1 occurrences migrated to: https://www.spring.io ([https](https://www.spring.io) result 301). * [ ] http://www.spring.io/ with 2 occurrences migrated to: https://www.spring.io/ ([https](https://www.spring.io/) result 301). * [ ] http://www.springframework.org/schema/batch-integration/spring-batch-integration.xsd with 2 occurrences migrated to: https://www.springframework.org/schema/batch-integration/spring-batch-integration.xsd ([https](https://www.springframework.org/schema/batch-integration/spring-batch-integration.xsd) result 301). * [ ] http://www.springframework.org/schema/batch/spring-batch.xsd with 2 occurrences migrated to: https://www.springframework.org/schema/batch/spring-batch.xsd ([https](https://www.springframework.org/schema/batch/spring-batch.xsd) result 301). * [ ] http://www.springframework.org/schema/beans/spring-beans-2.5.xsd with 3 occurrences migrated to: https://www.springframework.org/schema/beans/spring-beans-2.5.xsd ([https](https://www.springframework.org/schema/beans/spring-beans-2.5.xsd) result 301). * [ ] http://www.springframework.org/schema/beans/spring-beans-3.1.xsd with 1 occurrences migrated to: https://www.springframework.org/schema/beans/spring-beans-3.1.xsd ([https](https://www.springframework.org/schema/beans/spring-beans-3.1.xsd) result 301). * [ ] http://www.springframework.org/schema/beans/spring-beans-3.2.xsd with 1 occurrences migrated to: https://www.springframework.org/schema/beans/spring-beans-3.2.xsd ([https](https://www.springframework.org/schema/beans/spring-beans-3.2.xsd) result 301). * [ ] http://www.springframework.org/schema/tool/spring-tool-2.5.xsd with 1 occurrences migrated to: https://www.springframework.org/schema/tool/spring-tool-2.5.xsd ([https](https://www.springframework.org/schema/tool/spring-tool-2.5.xsd) result 301). * [ ] http://www.springframework.org/schema/tool/spring-tool-3.1.xsd with 2 occurrences migrated to: https://www.springframework.org/schema/tool/spring-tool-3.1.xsd ([https](https://www.springframework.org/schema/tool/spring-tool-3.1.xsd) result 301). * [ ] http://www.springframework.org/test with 34 occurrences migrated to: https://www.springframework.org/test ([https](https://www.springframework.org/test) result 301). * [ ] http://www.w3.org/TR/css3-selectors/ with 2 occurrences migrated to: https://www.w3.org/TR/css3-selectors/ ([https](https://www.w3.org/TR/css3-selectors/) result 301). * [ ] http://www.w3.org/TR/css3-syntax/ with 1 occurrences migrated to: https://www.w3.org/TR/css3-syntax/ ([https](https://www.w3.org/TR/css3-syntax/) result 301). * [ ] http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx with 2 occurrences migrated to: https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx ([https](https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx) result 302). * [ ] http://sourceforge.net/projects/cglib/files/cglib3/3.0/cglib-3.0.jar/download with 1 occurrences migrated to: https://sourceforge.net/projects/cglib/files/cglib3/3.0/cglib-3.0.jar/download ([https](https://sourceforge.net/projects/cglib/files/cglib3/3.0/cglib-3.0.jar/download) result 302). * [ ] http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context with 1 occurrences migrated to: https://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context ([https](https://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context) result 302). * [ ] http://www.springsource.org/download with 1 occurrences migrated to: https://www.springsource.org/download ([https](https://www.springsource.org/download) result 302). # Ignored These URLs were intentionally ignored. * http://test with 2 occurrences * http://www.springframework.org/schema/batch with 15 occurrences * http://www.springframework.org/schema/batch-integration with 8 occurrences * http://www.springframework.org/schema/batch/test with 2 occurrences * http://www.springframework.org/schema/beans with 26 occurrences * http://www.springframework.org/schema/integration with 6 occurrences * http://www.springframework.org/schema/tool with 15 occurrences * http://www.w3.org/1999/XSL/Format with 4 occurrences * http://www.w3.org/1999/XSL/Transform with 3 occurrences * http://www.w3.org/2001/XMLSchema with 10 occurrences * http://www.w3.org/2001/XMLSchema-instance with 12 occurrences * http://xmlns.jcp.org/xml/ns/javaee with 8 occurrences
398 lines
13 KiB
Plaintext
398 lines
13 KiB
Plaintext
:batch-asciidoc: ./
|
|
:toc: left
|
|
:toclevels: 4
|
|
|
|
[[testing]]
|
|
|
|
== Unit Testing
|
|
|
|
ifndef::onlyonetoggle[]
|
|
include::toggle.adoc[]
|
|
endif::onlyonetoggle[]
|
|
|
|
As with other application styles, it is extremely important to
|
|
unit test any code written as part of a batch job. The Spring core
|
|
documentation covers how to unit and integration test with Spring in great
|
|
detail, so it is not be repeated here. It is important, however, to think
|
|
about how to 'end to end' test a batch job, which is what this chapter
|
|
covers. The spring-batch-test project includes classes that
|
|
facilitate this end-to-end test approach.
|
|
|
|
[[creatingUnitTestClass]]
|
|
|
|
|
|
=== Creating a Unit Test Class
|
|
|
|
In order for the unit test to run a batch job, the framework must
|
|
load the job's `ApplicationContext`. Two annotations are used to trigger
|
|
this behavior:
|
|
|
|
|
|
* `@RunWith(SpringRunner.class)`:
|
|
Indicates that the class should use Spring's JUnit facilities
|
|
|
|
|
|
* `@ContextConfiguration(...)`:
|
|
Indicates which resources to configure the `ApplicationContext` with.
|
|
|
|
Starting from v4.1, it is also possible to inject Spring Batch test utilities
|
|
like the `JobLauncherTestUtils` and `JobRepositoryTestUtils` in the test context
|
|
using the `@SpringBatchTest` annotation.
|
|
|
|
The following example shows the annotations in use:
|
|
|
|
.Using Java Configuration
|
|
[source, java, role="javaContent"]
|
|
----
|
|
@SpringBatchTest
|
|
@RunWith(SpringRunner.class)
|
|
@ContextConfiguration(classes=SkipSampleConfiguration.class)
|
|
public class SkipSampleFunctionalTests { ... }
|
|
----
|
|
|
|
.Using XML Configuration
|
|
[source, java, role="xmlContent"]
|
|
----
|
|
@SpringBatchTest
|
|
@RunWith(SpringRunner.class)
|
|
@ContextConfiguration(locations = { "/simple-job-launcher-context.xml",
|
|
"/jobs/skipSampleJob.xml" })
|
|
public class SkipSampleFunctionalTests { ... }
|
|
----
|
|
|
|
[[endToEndTesting]]
|
|
|
|
|
|
=== End-To-End Testing of Batch Jobs
|
|
|
|
'End To End' testing can be defined as testing the complete run of a
|
|
batch job from beginning to end. This allows for a test that sets up a
|
|
test condition, executes the job, and verifies the end result.
|
|
|
|
In the following example, the batch job reads from the database and
|
|
writes to a flat file. The test method begins by setting up the database
|
|
with test data. It clears the CUSTOMER table and then inserts 10 new
|
|
records. The test then launches the `Job` by using the
|
|
`launchJob()` method. The
|
|
`launchJob()` method is provided by the
|
|
`JobLauncherTestUtils` class. The `JobLauncherTestUtils` class also provides the
|
|
`launchJob(JobParameters)` method, which
|
|
allows the test to give particular parameters. The
|
|
`launchJob()` method returns the
|
|
`JobExecution` object, which is useful for asserting
|
|
particular information about the `Job` run. In the
|
|
following case, the test verifies that the `Job` ended
|
|
with status "COMPLETED":
|
|
|
|
.XML Based Configuration
|
|
[source, java, role="xmlContent"]
|
|
----
|
|
@SpringBatchTest
|
|
@RunWith(SpringRunner.class)
|
|
@ContextConfiguration(locations = { "/simple-job-launcher-context.xml",
|
|
"/jobs/skipSampleJob.xml" })
|
|
public class SkipSampleFunctionalTests {
|
|
|
|
@Autowired
|
|
private JobLauncherTestUtils jobLauncherTestUtils;
|
|
|
|
private SimpleJdbcTemplate simpleJdbcTemplate;
|
|
|
|
@Autowired
|
|
public void setDataSource(DataSource dataSource) {
|
|
this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
|
|
}
|
|
|
|
@Test
|
|
public void testJob() throws Exception {
|
|
simpleJdbcTemplate.update("delete from CUSTOMER");
|
|
for (int i = 1; i <= 10; i++) {
|
|
simpleJdbcTemplate.update("insert into CUSTOMER values (?, 0, ?, 100000)",
|
|
i, "customer" + i);
|
|
}
|
|
|
|
JobExecution jobExecution = jobLauncherTestUtils.launchJob();
|
|
|
|
|
|
Assert.assertEquals("COMPLETED", jobExecution.getExitStatus().getExitCode());
|
|
}
|
|
}
|
|
----
|
|
|
|
.Java Based Configuration
|
|
[source, java, role="javaContent"]
|
|
----
|
|
@SpringBatchTest
|
|
@RunWith(SpringRunner.class)
|
|
@ContextConfiguration(classes=SkipSampleConfiguration.class)
|
|
public class SkipSampleFunctionalTests {
|
|
|
|
@Autowired
|
|
private JobLauncherTestUtils jobLauncherTestUtils;
|
|
|
|
private SimpleJdbcTemplate simpleJdbcTemplate;
|
|
|
|
@Autowired
|
|
public void setDataSource(DataSource dataSource) {
|
|
this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
|
|
}
|
|
|
|
@Test
|
|
public void testJob() throws Exception {
|
|
simpleJdbcTemplate.update("delete from CUSTOMER");
|
|
for (int i = 1; i <= 10; i++) {
|
|
simpleJdbcTemplate.update("insert into CUSTOMER values (?, 0, ?, 100000)",
|
|
i, "customer" + i);
|
|
}
|
|
|
|
JobExecution jobExecution = jobLauncherTestUtils.launchJob();
|
|
|
|
|
|
Assert.assertEquals("COMPLETED", jobExecution.getExitStatus().getExitCode());
|
|
}
|
|
}
|
|
----
|
|
|
|
[[testingIndividualSteps]]
|
|
|
|
|
|
=== Testing Individual Steps
|
|
|
|
For complex batch jobs, test cases in the end-to-end testing
|
|
approach may become unmanageable. It these cases, it may be more useful to
|
|
have test cases to test individual steps on their own. The
|
|
`JobLauncherTestUtils` class contains a method called
|
|
`launchStep`, which takes a step name and runs just
|
|
that particular `Step`. This approach allows for more
|
|
targeted tests letting the test set up data for only that step and
|
|
to validate its results directly. The following example shows how to use the
|
|
`launchStep` method to load a `Step` by name:
|
|
|
|
|
|
[source, java]
|
|
----
|
|
JobExecution jobExecution = jobLauncherTestUtils.launchStep("loadFileStep");
|
|
----
|
|
|
|
|
|
|
|
=== Testing Step-Scoped Components
|
|
|
|
Often, the components that are configured for your steps at runtime
|
|
use step scope and late binding to inject context from the step or job
|
|
execution. These are tricky to test as standalone components, unless you
|
|
have a way to set the context as if they were in a step execution. That is
|
|
the goal of two components in Spring Batch:
|
|
`StepScopeTestExecutionListener` and
|
|
`StepScopeTestUtils`.
|
|
|
|
The listener is declared at the class level, and its job is to
|
|
create a step execution context for each test method, as shown in the following example:
|
|
|
|
|
|
[source, java]
|
|
----
|
|
@ContextConfiguration
|
|
@TestExecutionListeners( { DependencyInjectionTestExecutionListener.class,
|
|
StepScopeTestExecutionListener.class })
|
|
@RunWith(SpringRunner.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());
|
|
}
|
|
|
|
}
|
|
----
|
|
|
|
There are two `TestExecutionListeners`. One
|
|
is the regular Spring Test framework, which handles dependency injection
|
|
from the configured application context to inject the reader. The
|
|
other is the Spring Batch
|
|
`StepScopeTestExecutionListener`. It works by looking
|
|
for a factory method in the test case for a
|
|
`StepExecution`, using that as the context for
|
|
the test method, as if that execution were active in a `Step` at runtime. The
|
|
factory method is detected by its signature (it must return a
|
|
`StepExecution`). If a factory method is not provided,
|
|
then a default `StepExecution` is created.
|
|
|
|
Starting from v4.1, the `StepScopeTestExecutionListener` and
|
|
`JobScopeTestExecutionListener` are imported as test execution listeners
|
|
if the test class is annotated with `@SpringBatchTest`. The preceding test
|
|
example can be configured as follows:
|
|
|
|
[source, java]
|
|
----
|
|
@SpringBatchTest
|
|
@RunWith(SpringRunner.class)
|
|
@ContextConfiguration
|
|
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());
|
|
}
|
|
|
|
}
|
|
----
|
|
|
|
The listener approach is convenient if you want the duration of the
|
|
step scope to be the execution of the test method. For a more flexible
|
|
but more invasive approach, you can use the
|
|
`StepScopeTestUtils`. The following example counts the
|
|
number of items available in the reader shown in the previous example:
|
|
|
|
|
|
[source, java]
|
|
----
|
|
int count = StepScopeTestUtils.doInStepScope(stepExecution,
|
|
new Callable<Integer>() {
|
|
public Integer call() throws Exception {
|
|
|
|
int count = 0;
|
|
|
|
while (reader.read() != null) {
|
|
count++;
|
|
}
|
|
return count;
|
|
}
|
|
});
|
|
----
|
|
|
|
[[validatingOutputFiles]]
|
|
|
|
|
|
=== Validating Output Files
|
|
|
|
When a batch job writes to the database, it is easy to query the
|
|
database to verify that the output is as expected. However, if the batch
|
|
job writes to a file, it is equally important that the output be verified.
|
|
Spring Batch provides a class called `AssertFile` to
|
|
facilitate the verification of output files. The method called
|
|
`assertFileEquals` takes two
|
|
`File` objects (or two
|
|
`Resource` objects) and asserts, line by line, that
|
|
the two files have the same content. Therefore, it is possible to create a
|
|
file with the expected output and to compare it to the actual
|
|
result, as shown in the following example:
|
|
|
|
|
|
[source, java]
|
|
----
|
|
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));
|
|
----
|
|
|
|
[[mockingDomainObjects]]
|
|
|
|
|
|
=== Mocking Domain Objects
|
|
|
|
Another common issue encountered while writing unit and integration
|
|
tests for Spring Batch components is how to mock domain objects. A good
|
|
example is a `StepExecutionListener`, as illustrated
|
|
in the following code snippet:
|
|
|
|
|
|
[source, java]
|
|
----
|
|
public class NoWorkFoundStepExecutionListener extends StepExecutionListenerSupport {
|
|
|
|
public ExitStatus afterStep(StepExecution stepExecution) {
|
|
if (stepExecution.getReadCount() == 0) {
|
|
return ExitStatus.FAILED;
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
----
|
|
|
|
The preceding listener example is provided by the framework and checks a
|
|
`StepExecution` for an empty read count, thus
|
|
signifying that no work was done. While this example is fairly simple, it
|
|
serves to illustrate the types of problems that may be encountered when
|
|
attempting to unit test classes that implement interfaces requiring Spring
|
|
Batch domain objects. Consider the following unit test for the listener's in the preceding example:
|
|
|
|
|
|
[source, java]
|
|
----
|
|
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());
|
|
}
|
|
----
|
|
|
|
Because the Spring Batch domain model follows good object-oriented
|
|
principles, the `StepExecution` requires a
|
|
`JobExecution`, which requires a
|
|
`JobInstance` and
|
|
`JobParameters`, to create a valid
|
|
`StepExecution`. While this is good in a solid domain
|
|
model, it does make creating stub objects for unit testing verbose. To
|
|
address this issue, the Spring Batch test module includes a factory for
|
|
creating domain objects: `MetaDataInstanceFactory`.
|
|
Given this factory, the unit test can be updated to be more
|
|
concise, as shown in the following example:
|
|
|
|
|
|
[source, java]
|
|
----
|
|
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());
|
|
}
|
|
----
|
|
|
|
The preceding method for creating a simple
|
|
`StepExecution` is just one convenience method
|
|
available within the factory. A full method listing can be found in its
|
|
link:$$https://docs.spring.io/spring-batch/apidocs/org/springframework/batch/test/MetaDataInstanceFactory.html$$[Javadoc].
|