Friday, November 06, 2009

Unique maven surefire plugin configurations for individual tests




Here's what I want to do:

I have a maven project, which has got multiple unit-tests, db-tests (e.g. DAO tests) and integration/system tests. One of the unit tests require high memory (e.g. 512 MB), but other unit tests runs fine with the default heap size (and doesn't need large heap).
I want to run all unit tests by default in the test phase of the build cycle, run the db-tests only when I know a db is present and run the system tests in the integration phase of the build cycle.

Lets assume some names for the tests:

1) Names of all unit tests end with Test.java, e.g. ClientTest.java, SomeLogicTest.java etc
2) The name of the unit test that require high-memory is TestTheWorldWithBigMemoryTest.java. Note that this is not a system tests, its a unit test and just that it requires large heap to run.
3) All db-tests end with DaoTest.java, e.g. UserDaoTest.java, CatalogDaoTest.java etc
4) All system/integration tests end with SystemTest.java, e.g. AddToShoppingCartSystemTest.java, PurchaseSystemTest.java etc

OK, so here's how to configure your pom to do it:



<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>example</groupId>
<artifactId>MySampleProject</artifactId>
<packaging>jar</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>MySampleProject</name>
<url>http://www.abhisanoujam.blogspot.com/</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<inherited>true</inherited>
<configuration>
<!--
Skip tests by default. Will be using different executions
setup to run different set of tests with different
configurations
-->
<skip>true</skip>
<!-- other default configuration for all the tests, just an example -->
<reportFormat>plain</reportFormat>
<systemProperties>
<property>
<name>some.property.used.by.tests</name>
<value>what.you.want.value</value>
</property>
</systemProperties>
</configuration>
<executions>
<execution>
<!-- run all tests except for system tests and the big memory test -->
<id>test-phase-execution</id>
<phase>test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<skip>false</skip>
<includes>
<include>**/*Test.java</include>
</includes>
<excludes>
<!-- exclude inner classes -->
<exclude>**/*$*</exclude>
<!-- exclude the test that need large heap -->
<exclude>**/TestTheWorldWithBigMemoryTest.java</exclude>
<!-- exclude the system-tests -->
<exclude>**/*SystemTest.java</exclude>
<!-- exclude the db-tests -->
<exclude>**/*DaoTest.java</exclude>
</excludes>
</configuration>
</execution>
<execution>
<!-- Run tests with 512 MB heap -->
<id>large-heap-test-execution</id>
<phase>test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<skip>false</skip>
<includes>
<!--
You can even follow some pattern for these kind of
tests and use the pattern here
-->
<!-- For example, **/*BigMemoryTest.java -->
<include>**/TestTheWorldWithBigMemoryTest.java</include>
</includes>
<excludes>
<exclude>**/*$*</exclude>
</excludes>
<argLine>-Xms512m -Xmx512m</argLine>
</configuration>
</execution>
<execution>
<!-- Run the system tests in the integration-phase -->
<id>system-tests-execution</id>
<phase>integration-test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<skip>false</skip>
<includes>
<include>**/*SystemTest.java</include>
</includes>
<excludes>
<exclude>**/*$*</exclude>
</excludes>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

<profiles>

<!-- Profile for running database tests -->
<profile>
<id>test-db</id>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<groupId>org.apache.maven.plugins</groupId>
<configuration>
<!--
Skip tests as the other tests have been already executed
in the "test" and "integration-test" phases
-->
<skip>true</skip>
</configuration>
<executions>
<execution>
<id>db-test-execution</id>
<phase>test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<skip>false</skip>
<includes>
<!-- We only need to include the db tests here -->
<include>**/*DaoTest.java</include>
</includes>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>

</profiles>

</project>





I'll try to explain in words (not xml ;-) ) what I did above:
By default, maven runs the "test" goal of maven surefire plugin. First we tell it to skip tests by adding the plugin in the <build> section of the pom and specifying <skip>true</skip> in the main configuration of the plugin. Here, you can specify other configurations that you want for your tests as a whole.
Then we specify other executions for the plugin and play around with the include/exclude pattern and the configurations for each setup. We can setup multiple executions and also have different set of configurations for each execution setup. Just as a note, adding multiple <plugin> sections for the same plugin in the build section does not work, in fact this is the sole reason of this blog post, and to figure out how to have different configurations depending on your requirements for the same plugin.
In the "test-phase-execution", we include all tests with the **/*Test and exclude the big memory test, dao tests and the system tests.
For the unit-test that requires large heap, its just a matter of adding <argLine>-Xms512m -Xmx512m</argLine> in the configuration for that execution setup. Also we include only that test to run in the include tag.
We bind another execution in the "integration-test" phase to run the system tests by matching the include pattern to only system tests.

We add another profile "test-db", which we can use when we know we have a database is up and running (you don't want your dao tests to fail all the time during development when you don't have a DB running in your environment). In the profile, we again set up the maven-surefire plugin to execute only the dao tests by playing around with the include/exclude pattern. You can activate this profile whenever you want to run the dao tests.

You can check-out a very simple project from here and see the above pom in action.

Here's how you would run them:

mvn clean package
-- This will run all the unit-tests (including the big memory test) but not the system tests and the dao tests

mvn -Ptest-db clean package
-- This will the all the unit tests (like above) and also run the dao tests. This still excludes the system test.

mvn clean install
-- This will run all the unit tests + the system tests. This won't run the dao tests.

mvn -Ptest-db clean install
-- This will run all the tests -- unit tests, big memory test, dao tests and the system tests

Regarding the system/integration tests, instead of doing like above, you can separate all your system-tests in a separate sub-module too, which I guess is more appropriate when your project is kind of large.

Enjoy...