It's very hard to 'unit' test a spring batch job, purely because there is so much interaction with external resources, such as databases or files etc. The approach I have has two levels:
The individual methods in the tasklets are unit tested as normal, I just call the methods directly from a JUnit test, with the usual mocks or stubs.
Secondly, I run the batch from a JUnit test, by calling the main method directly, with all of the necessary infrastructure in place. For my batch jobs, I need a database and some input files. My JUnit test copies all of the necessary files for that test into a temp directory (I use maven so usually target/it), along with a database (I have copies of my MySQL database saved in HSQLDB format). I then call Main.main() directly with the correct parameters, let the batch run and then check the results, check to see if the correct files have been generated, the database has been changed correctly etc.
This has a couple of advantages as a way of working.
- I can run these tests from Eclipse, so shortening the debugging cycle, because I don't need to build the full package each time to test it.
- They are easy to incoporate into the build, I just include them in the failsafe plugin in maven.
With a couple of warnings:
You'll need to run the main method within a SecurityManager. If your main calls System.exit(), you don't want the JVM to stop. For an example of a SecurityManager, see org.junit.tests.running.core.MainRunner. This is called like:
Integer exitValue = new MainRunner().runWithCheckForSystemExit(new Runnable() {
public void run() {
Main.main(new String[] {});
}
});
Using the above, you can assert that a batch calls System.exit() with the correct values on failure, allowing testing of failure conditions as well.
Secondly, you can do a lot of setup from a JUnit test, but you can't do it all, so if for instance, you require an FTP server, I usually start it from maven rather than the JUnit, before the failsafe plugin runs. This can be a little bit fiddly sometimes.