8/11/2009
Thanks to a Stack Overflow question, I recently stumbled over Spring's integration test support (documentation here). In short, it provides a Spring context of your choice to your jUnit or TestNG classes and adds support for the @Transactional annotation.
However, contrary to the usual behaviour – comitting a transaction if no exception occurred - every transaction is instead rolled back by default, leaving the database unchanged.
All you have to do is to tell JUnit to use Spring's test runner class, tell
Spring which context files to load, and add the @Transactional annotation to
your class or test methods.
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations={"/integrationTestContext.xml"}) @Transactional public class MyIntegrationTest { @Test public void myTestMethod() { ... } }
This is a rather elegant way of using the IoC pattern for integration testing. It even recycles the Spring container, should you use the same configuration in multiple test cases and/or classes.
However, if you also use Maven, you will have noticed that its opinion on
integration testing seems to be "we're not really doing that here". While
there is an integration-test phase, there is no distinct place to keep
integration tests. While it seems that maybe, some day, there will be
support for a "src/it/java" source folder to hold integration test classes, as
of now we have some configuration work to do to run unit and integration tests
separately.
In our pom.xml, we re-configure the Maven Surefire plugin as follows:
<plugin> <artifactId>maven-surefire-plugin</artifactId> <configuration> <excludes> <exclude>**/itest/**</exclude> </excludes> </configuration> <executions> <execution> <id>surefire-itest</id> <phase>integration-test</phase> <goals> <goal>test</goal> </goals> <configuration> <excludes> <exclude>none</exclude> </excludes> <includes> <include>**/itest/**</include> </includes> </configuration> </execution> </executions> </plugin>
The ugly bit here is that we're forced to keep the integration tests in the
same source folder as the unit tests, namely src/test. In this example, we
exclude tests that have "itest" somewhere in their package name from unit
testing, and bind the surefire plugin to the "integration-test" phase, running
only tests with "itest" in their package name. I went for putting all
integration tests into a root /itest package, which makes them easy to tell
apart, and run separately, from the unit tests.
With this layout, your test classes would, as previously, reside within the
same package as the classes that they test, e.g. my.package.MyClassTest,
while integration tests reside in their own package, e.g.
itest.my.package.MyClassIntegrationTest.
We have now established a working distinction between fast-running unit tests and slower integration tests requiring a Spring container, and possibly additional resources. Certainly not ideal, but doable.