I'm looking for best practices for setting up unit and integration tests using Spring.
I usually use 3 kind of tests:
- "real" unit tests (no dependencies)
- tests run either as "unit" test (in-memory db, local calls, mock objects,...) or as integration test (persistent db, remote calls,...)
- tests run only as integration tests
Currently I only have tests of the second category, which is the tricky part. I set-up a base test class like:
@ContextConfiguration(locations = { "/my_spring_test.xml" })
public abstract class AbstractMyTestCase extends AbstractJUnit4SpringContextTests
And "unit" tests like:
public class FooTest extends AbstractMyTestCase
with autowired attributes.
What's the best way to run the test in a different (integration test) environment? Subclass the test and override the ContextConfiguration?
@ContextConfiguration(locations = { "/my_spring_integration_test.xml" })
public class FooIntegrationTest extends FooTest
Would this work (I cannot currently easily test it here)? The problem with this approach is that "@ContextConfiguration(locations = { "/my_spring_integration_test.xml" })" is duplicated a lot.
Any suggestions?
Regards, Florian
I'd go with this version:
and in
my_spring_test.xml
, I'd use thePropertyPlaceHolderConfigurer
mechanism.Example for JPA:
Now all you need to do is have different versions of test.properties on the class path for in-memory and real integration tests (and of course the respective driver classes need to be present). You can even set system properties to overwrite the property values.
If you want to prepare this with maven, you will find that copying files with maven is not trivial. You will need a way to execute code, the standard choices being the maven-antrun-plugin and gmaven-maven-plugin.
Either way: have two configuration files, e.g. in src/main/config and add two plugin executions, one in phase
generate-test-resources
and one in phasepre-integration-test
. Here's the GMaven version:I recently ran in to the same problem and looking at the documentation for the @ContextConfiguration annotation, I noticed the inheritLocations option.
By adding this to my class e.g.
I found that I was able to override the ContextConfiguration as desired.
I extended the GenericXmlContextLoader
public class MyContextLoader extends GenericXmlContextLoader {
and overrote the
protected String[] generateDefaultLocations(Class<?> clazz)
method to collect the config file names of a directory which I can specify by a SystemProperty (-Dtest.config=).
I also modified the follwowing method to NOT modify any locations
I use this context loader like this
Running the test with a SystemProperty indicating the source of the config files enables you now to use completely different configurations.
The usage of a SystemProperty is of course only one strategy to specify the configuration location. You can do whatever you want in
generateDefaultLocations()
.EDIT:
This solution enables you to use complete different application context configurations (e.g. for mock objects) and not only different properties. You do not need a build step to deploy everything to your "classpath" location. My concrete implementation also used the users name as default to look for a configuration directory (src/test/resources/{user}) if no system property is given (makes it easy to maintain specific test environments for all developers on the project).
The usage of the PropertyPlaceholder ist still possible and recommended.
EDIT:
Spring Version 3.1.0 will support XML profiles/Environment Abstraction which is similar to my solution and will enable the choice of configuration files for different environments/profiles.
I have had no success in using Spring 3.x context:property-placeholder tag. I have used the old fashion bean tag along with a properties file and was able to set up a connection between my code and my database like so:
Here's an example of the properties file:
Then I set up my JUnit test like so:
That worked for me, but everybody does things a little differently. Hope this helps.