I would like to thank Arnaud Heritier who gave me plenty of tips and advices to write this blog. In fact if you want to endorse someone on Maven in LinkedIn, he is the man, not me ;o)
If you use Maven in your projects you might wonder what “Let’s Turn Integration Tests with Maven into a First-Class Citizen” means. A bit of introduction on that. Maven is based around the central concept of a build lifecycle and if you look at the default lifecycle bindings you see the following phases :
- process-resources
- compile
- process-test-resources
- test-compile
- test
- package
- install
- deploy
As you can see, unit tests are central in Maven. Package your project with mvn package or deploy it with mvn deploy and this will kick the test phase. Why ? Because in the default lifecycle bindings of Maven, the test phase is automatically called through the Surefire plugin. The maven-surefire-plugin is designed for running unit tests and if any of the tests fail then it will fail the build immediately.
So where are the integration tests ? Not in the default lifecycle, that’s for sure. They are handled by a different plugin : Failsafe. The maven-failsafe-plugin is a fork of Surefire designed to run integration tests (after the package phase, on the integration-test phase). But on the contrary of Surefire, Failsafe is not activate by default, you need to explicitelly add it to you pom.xml and attach a phase to it :
[sourcecode language=”xml” highlight=”8,9″]
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.12.4</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
[/sourcecode]
The Maven lifecycle has four phases for running integration tests:
- pre-integration-test: on this phase you can start any required service or do any action (starting a database, a webserver…)
- integration-test: failsafe will run the test on this phase
- post-integration-test: time to shutdown all services
- verify: failsafe runs another goal that interprets the results of tests here
The Failsafe Plugin has only 2 goals:
- failsafe:integration-test : runs the integration tests of an application.
- failsafe:verify : verifies that the integration tests of an application passed.
By default, the Surefire plugin executes **/Test*.java, **/*Test.java, and **/*TestCase.java test classes. The Failsafe plugin will look for **/IT*.java, **/*IT.java, and **/*ITCase.java. So if you are using unit test and integration tests, make sure to put them all in src/test/java and use this naming convention.
How does this work ? You run mvn test : unit test are executed. You run mvn verify : unit test and then integration tests are executed. But if unit test fail, integration tests are not passed. Meaning that, for Maven, unit tests are more important than integration test.
How to seperate unit and integration test ?
What I really want is to be able to, either run UT or IT or none of them. Something like :
- mvn install -DskipUTs : Skips Unit tests
- mvn install -DskipITs : Skips Integration tests
- mvn install -DskipTests : Skips both Unit and Integration Tests
Here is a little trick you need to do in your pom.xml (again, thanks Arnaud)
[sourcecode language=”xml” highlight=”8,9″]
<project>
…
<properties>
<skipTests>false</skipTests>
<skipITs>${skipTests}</skipITs>
<skipUTs>${skipTests}</skipUTs>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.12.4</version>
<configuration>
<skipTests>${skipTests}</skipTests>
<skipITs>${skipITs}</skipITs>
</configuration>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12.4</version>
<configuration>
<skipTests>${skipUTs}</skipTests>
</configuration>
</plugin>
</plugins>
</build>
</project>
[/sourcecode]
Conclusion
Unit testing is at the heart of Maven… but not integration testing. With this little trick you can put UT and IT at the same level : you can run either both tests, UT only, IT only or none. But it would be good to have this behaviour by default, without any configuration. That would mean adding the integration-test phase to the default lifecycle bindings of Maven.
What do you think ? Shall I start a discussion on the Maven Mailing List ? Create a JIRA issue ? Shall we put integration tests at the same level of unit test ? Any other idea of how we could integrate both more closely ?

