To unit test a MOJO create a JUnit test case and extend
AbstractMojoTestCase. It comes with the
<dependency> <groupId>org.apache.maven.plugin-testing</groupId> <artifactId>maven-plugin-testing-harness</artifactId> <version>1.2</version> <scope>test</scope> </dependency>Note that version 1.2 of the
maven-plugin-testing-harnessis still Maven 2.0 compatible, as will be version 1.3. The alpha release of version 2.0 is already based on Maven 3. In the test case the MOJO is initialised with a pom fragment and executed. The basic usage is well documented in the Maven Plugin Harness documentation. The usual folder structure for plugin unit tests is
. |-- pom.xml \-- src |-- main \-- test |-- java | \-- MojoTest.java \-- resources \-- unit |-- test-configuration1 | \-- resources for this test if any |-- test-configuration1-plugin-config.xml \-- test-configuration2-plugin-config.xmlThe class
testConfiguration2(). There are several Maven projects using this approach, just do a code search.
If your MOJO needs more complex parameters, e.g. a
ArtifactRepository, these have to be provided as stubs. Stubs are simple implementations of real Maven objects, e.g.
public class org.apache.maven.plugin.testing.stubs.ArtifactStub implements org.apache.maven.artifact.Artifactand have to be configured in the pom fragment (
test-configuration1-plugin-config.xml) as described in the Maven Plugin Testing Harness Cookbook. See also MackerMojoTest in the Macker Maven Plugin for an extensive example. There are several stubs to simulate Maven objects such as
ArtifactResolver. Creating stubs gets cumbersome when you need more of Maven's internals. Every object and every method has to be stubbed out.
Integration testing is done with the
<dependency> <groupId>org.apache.maven.plugin-testing</groupId> <artifactId>maven-plugin-testing-harness</artifactId> <version>1.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.maven.plugin-testing</groupId> <artifactId>maven-plugin-testing-tools</artifactId> <version>1.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.codehaus.plexus</groupId> <artifactId>plexus-utils</artifactId> <version>1.5.6</version> <scope>test</scope> </dependency>Note that the testing tools 1.2 specifically need
plexus-utilsversion 1.5.6. The usual folder structure for plugin integration tests is
. |-- pom.xml \-- src |-- main \-- test |-- java | \-- MojoIT.java \-- resources \-- it |-- test-case1 | |-- pom.xml | \-- main | \-- classes and data for this test \-- test-case2The class
MojoITcontains the test methods
test-case1contains a full Maven module that uses the MOJO under test in some way.
The test uses
BuildTooland the other tools from
maven-plugin-testing-toolsto create a local repository, install the Maven module under test into it and execute the Maven build of
test-case1/pom.xml. The Plugin Testing Tools site provides more detail about this process and the various tools.
The Plugin Testing Tools do not provide an abstract test case. Each test has to create it's own
AbstractPluginITCase. A good example is the AbstractEclipsePluginIT of the Eclipse Plugin. It contains methods to build the module, execute poms against it and verify created artefacts. As far as I know this is the only example available. AbstractOuncePluginITCase is a modified copy as well as AbstractMackerPluginITCase.
Integration tests should be executed in the
<build> <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> <executions> <execution> <phase>integration-test</phase> <goals> <goal>test</goal> </goals> <configuration> <includes> <include>**/*IT.java</include> </includes> <excludes> <exclude>specified only to override config from default execution</exclude> </excludes> </configuration> </execution> </executions> </plugin> </plugins> </build>The
pom.xmlto skip all tests, so the tests are not invoked again when the module is build by the integration test.
Help Mojo Workaround
Unfortunately there is a problem with the above approach. It works fine for modules that do not contain Maven Plugins, like my global ruleset. But it fails during integration test preparation when the
maven-plugin-pluginis executed. Fortunately the guys from the Eclipse Plugin found a workaround:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-plugin-plugin</artifactId> <!-- lock down to old version as newer version aborts build upon no mojos as required during ITs --> <version>2.4.3</version> <executions> <!-- disable execution, makes IT preparation using maven-plugin-testing-tools fail (see target/test-build-logs/setup.build.log) --> <execution> <id>help-mojo</id> <configuration> <extractors> <extractor /> </extractors> </configuration> </execution> </executions> </plugin>But now the plugin can't be built from scratch any more. So they used a profile to run the integration tests and disable
<profiles> <profile> <id>run-its</id> <build> <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> ... </plugin> <plugin> <artifactId>maven-plugin-plugin</artifactId> ... </plugin> </plugins> </build> </profile> </profiles>Wrap-up
So far so good. The
BuildToolhas to activate the profile
run-its(as it has to skip
help-mojoexecution). This could be done by setting a certain property, let's call it
ProjectTool:packageProjectArtifact. Then the profile would only be activated during integration test preparation.
<profiles> <profile> <id>run-its</id> ... <activation> <property> <name>ProjectTool:packageProjectArtifact</name> </property> </activation> </profile> </profiles>I've submitted a patch for that, but in the meantime I had to copy the
BuildToolinto my own plugin, ugh. (I was working towards a clean solution throughout this post but in the end all gets messed up.) The whole plugin testing can be seen in action in the Macker Maven Plugin.
Experimenting with the Maven Plugin Testing Tools was part of a System One Research Day. Thank you System One for supporting Open Source :-)