Thomas Sundberg

September 20, 2014

Making life easier with a multi module Maven project

Filed under: Automation, Maven — Tags: , , , — Thomas Sundberg @ 00:02

This post has been migrated to

Working with slow modules in Maven is a problem. People will not build the module as often as they need and they will therefore not find problems as early as they could.

A solution could be to separate some of the slow stuff to a separate module. One separation can be to have a specific module for slow tests. This will, however, not solve the problem, that the module is too slow.

A solution to the problem could be to only include it in the execution when you invoke a specific Maven profile. This would separate the execution of a slow module from the execution of the rest, fast, modules.

Let me implement a simple example with two modules. There is the first module, the application, that we always want to build. It has fast unit tests and it is therefore not hindering to execute it often. Then there is the second module, the acceptance tests. It requires you to fire up your application before it can be executed. It is therefore dead slow. As a developer you will probably only want to execute the acceptance test module now and then.

Let me show you one way to achieve this.

My solution is to have a Maven profile that includes the acceptance test module. The normal execution doesn’t include this slow module, it will only be executed when I explicitly request it.

My parent pom looks like this:


<project xmlns="" xmlns:xsi="" xsi:schemaLocation=""> <modelVersion>4.0.0</modelVersion> <groupId>se.thinkcode</groupId> <artifactId>my-great-application-parent</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging> <modules> <module>application</module> </modules> <profiles> <profile> <id>acceptance</id> <modules> <module>acceptance-test</module> </modules> </profile> </profiles> <dependencyManagement> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> </dependencies> </dependencyManagement> </project>

It will always do whatever I tell Maven to do with the application module. It will only include the acceptance-test module when I call Maven explicitly with the profile acceptance. In other words, calling Maven like this:

mvn clean install

will perform clean and install on the application module. Invoking Maven like this:

mvn clean install -P acceptance

will perform clean and install on both the application and the acceptance-test module and in that order.

This separation can make the life for the developers better. The developers will be able to iterate faster while developing the application and when they feel that they need it, they can run all the tests and verify that they haven’t broken anything. The acceptance tests should only verify things that can’t be verified using unit test. The wiring in some applications can be an example.

When should the acceptance profile be executed, then? It should always be executed by your Continuous Integration, CI, server. It is usually you first safetynet and all tests should be executed here as often as possible. Typically a few seconds after each commit. (Or push if you use a modern version control system.)

If you are interested in implementing the entire example, this is the file structure I have used:

|-- acceptance-test
|   |-- pom.xml
|   `-- src
|       `-- test
|           `-- java
|               `--
|-- application
|   |-- pom.xml
|   `-- src
|       `-- test
|           `-- java
|               `--
|-- pom.xml

The implementations looks like this:


<project xmlns="" xmlns:xsi="" xsi:schemaLocation=""> <modelVersion>4.0.0</modelVersion> <parent> <groupId>se.thinkcode</groupId> <artifactId>my-great-application-parent</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>application</artifactId> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> </dependency> </dependencies> </project>


import org.junit.Test; import static junit.framework.TestCase.assertTrue; public class UnitTest { @Test public void shouldAlwaysBeInvoked() { assertTrue("Should never fail and will ensure that the unit tests are possible to execute", true); } }


<project xmlns="" xmlns:xsi="" xsi:schemaLocation=""> <modelVersion>4.0.0</modelVersion> <parent> <groupId>se.thinkcode</groupId> <artifactId>my-great-application-parent</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>acceptance-test</artifactId> <packaging>jar</packaging> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> </dependency> </dependencies> </project>


import org.junit.Test; import static junit.framework.TestCase.assertTrue; public class AcceptanceTest { @Test public void shouldAlwaysBeInvoked() { assertTrue("Should never fail and will ensure that the acceptance tests are possible to execute", true); } }


It can be argued that this division is bad. It can even be considered to be an anti pattern as described here by Karl-Heinz Marbaise.

I think, however, that the problem pointed out above is a problem with automation. You may end up in an unsynched state if you release manually and forget to release all modules. If you on the other hand always release using your Continuous Integration, CI, server, then it is a lot harder to end up with a mess described above.

As always, with great power comes great responsibility.

The solution that Karl-Heinz Marbaise suggests may be a valid option for you. You will have to decide for yourself what supports your development best.


Running slow test from Maven is possible to do without complicating the life for your developers. You must do whatever it takes to shorten the feedback loop and separating slow tests from fast test is one way to do it.


Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at

%d bloggers like this: