68
votes

I have a project which I am building with Maven which uses Hibernate (and Spring) to retrieve data from a database, etc.

My "tests" for the DAOs in my project extend Spring's AbstractTransactionalDataSourceSpringContextTests so that a DataSource can be wired into my class under test to be able to actually run the query/Hibernate logic, to fetch data, etc.

On several other projects I've used these types of test in concert with a HSQL database (either in-memory or pointed at a file) to be able to efficiently test the actual database querying logic without relying on an external database. This works great, since it avoids any external dependencies and the "state" of the database prior to running the tests (each of which are wrapped in a transaction which is rolled back) is well defined.

I'm curious though about the best way to organize these tests, which are really a loose flavor of integration tests, with Maven. It feels a bit dirty to keep these tests in src/test/java, but from what I've read there doesn't seem to be a consistent strategy or practice for organizing integration tests with Maven.

From what I've read so far, seems like I can use the Failsafe plugin (or a second instance of Surefire) and bind it to the integration-test phase, and that I can also bind custom start-up or shutdown logic (such as for starting/stopping the HSQL instance) to pre-integration-test or post-integration-test. But, is this really the best method?

So my question basically is - what is the generally accepted best practice on organizing this with Maven? I'm having trouble finding any sort of consistent answer in the documentation.

What I'd like is to:

  • Seperate unit tests from integration tests, so only unit tests are run during the test phase
  • The ability to bind custom startup/shutdown logic to pre-integration-test and post-integration-test
  • Have the reports from the integration-tests merged/presented with the unit test Surefire reports
4
Move the integration tests in a separate project and keep the unit tests in the same project as the source.Thorbjørn Ravn Andersen

4 Answers

21
votes

There is this codehaus page with some guidelines. I found the failsafe plugin a bit of a hack, and it makes running the unit tests in Eclipse fiendishly complicated. I do broadly what you're describing.

Define integration tests in src/itest/java In the pre-integration-test phase:

  • Clear target/test-classes
  • Use the build-helper-maven-plugin's add-test-source goal to add the itest source location
  • Use a custom Mojo to remove src/test/java from the configuration so the unit tests are not compiled again (I don't really like this, but it's needed to maintain the separation of unit and integration tests).
  • Use the compiler-plugin to compile the integration tests

Then in the integration-test phase, use the surefire-plugin to run the tests.

Finally, bind any tidy up goals to the post-integration-test phase (though normally they're not needed as you can use the test teardown() to tidy up).

I've not yet found a way to merge the test results as the reporting phase has passed, but I tend to view the integration tests as an added bonus, so as long as they pass the report is not so important.

Update: I think it's worth pointing out that you can run Jetty from within your integration tests rather than using a jetty goal. This gives you much finer control over the tests. You can get more details from this answer and the referenced blogs.

26
votes

A very simple way of doing this is to use JUnit categories.

You can then easily run some tests during the test phase and another during the integration-test phase.

It takes minutes and requires only 3 steps.

  1. Define a marker interface
  2. Annotate the classes you wish to split
  3. Configure Maven plugins.

A full example is given here. https://stackoverflow.com/a/10381662/1365383

7
votes

This good blog post suggests three options;

1) Separate module for integration tests

2) Different source directories

3) Different file name patterns

I'm yet to try all three, so can't offer an opinion on which I favour.

1
votes

I prefer the second option, Different source directories ,but I found quite annoying have to end with IT the integration tests or excluding packages.

To avoid this, I've ended up with this config:

<properties>
    <testSource>src/test/java</testSource>
    <testSourceResource>src/test/resources</testSourceResource>
</properties>
<build>
    <testSourceDirectory>${testSource}</testSourceDirectory>
    <testResources>
            <testResource>
            <directory>${testSourceResource}</directory>
            </testResource>
        </testResources>
.....
.....

and then I override both variables in different profiles for integration and acceptance test:

<profiles>
  <profile>
   <id>acceptance-tests</id>
   <properties>
    <testSource>src/acceptance-test/java</testSource>
    <testSourceResource>src/acceptance-test/resources</testSourceResource>
   </properties>
  </profile>
 <profile>
   <id>integration-tests</id>
    <properties>
    <testSource>src/integration-test/java</testSource>
    <testSourceResource>src/integration-test/resources</testSourceResource>
    </properties>
  </profile>
.....
.....
.....