Another Cryptic Message from Maven

I am still struggling with the development (and most of all, the tests) of a Maven plug-in. After I tried to run unit tests in my Eclipse, I got the following error.


java.lang.Exception: Unexpected exception, expected but was
at org.junit.internal.runners.statements.ExpectException.evaluate(ExpectException.java:31)
at org.apache.maven.plugin.testing.MojoRule$2.evaluate(MojoRule.java:308)
at org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:47)
at org.junit.rules.RunRules.evaluate(RunRules.java:18)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: org.codehaus.plexus.component.repository.exception.ComponentLookupException: java.util.NoSuchElementException
role: org.apache.maven.plugin.Mojo
roleHint: net.roboconf:roboconf-maven-plugin:1.0-SNAPSHOT:validate
at org.codehaus.plexus.DefaultPlexusContainer.lookup(DefaultPlexusContainer.java:264)
at org.codehaus.plexus.DefaultPlexusContainer.lookup(DefaultPlexusContainer.java:240)
at org.codehaus.plexus.PlexusTestCase.lookup(PlexusTestCase.java:205)
at org.apache.maven.plugin.testing.AbstractMojoTestCase.lookupMojo(AbstractMojoTestCase.java:389)
at org.apache.maven.plugin.testing.AbstractMojoTestCase.lookupMojo(AbstractMojoTestCase.java:334)
at org.apache.maven.plugin.testing.MojoRule.lookupMojo(MojoRule.java:164)
at net.roboconf.maven.ValidateMojoTest.testInvalidProject(ValidateMojoTest.java:48)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.internal.runners.statements.ExpectException.evaluate(ExpectException.java:22)
... 18 more
Caused by: java.util.NoSuchElementException
at java.util.Collections$EmptyIterator.next(Collections.java:3006)
at org.codehaus.plexus.DefaultPlexusContainer.lookup(DefaultPlexusContainer.java:260)
... 33 more

What matters here, is…

role: org.apache.maven.plugin.Mojo
roleHint: net.roboconf:roboconf-maven-plugin:1.0-SNAPSHOT:validate

What happened with my plug-in is that I had cleaned my Maven project.
After executing mvn clean test -X, I noticed the maven-plugin-plugin generates a descriptor from my mojos. The org.apache.maven.plugin.Mojo role is generated during this step. The role-hint is generated from my mojos. When I cleaned my project, I simply deleted the descriptor, which resulted in the error above.

After a refresh in Eclipse, I could go farther in my unit tests.
Conclusion: if you want to run the unit tests of your Maven plug-in in Eclipse, make sure it was compiled with Maven first.

Write Unit Tests for a Maven plug-in

Today, I started to write unit tests for a Maven plug-in I am working on.
For the moment, I created 3 mojos and registered them for a custom packaging I defined in the plexus file called components.xml.

Before implementing these mojos, I wanted to set up unit tests.
For this, I followed these explanations on Maven’s web site about the maven-plugin-testing-harness plug-in. The thing is that it just did not work.

So, hopefully, I hope this post will help those in troubles.
First, you must add in your POM dependencies that are marked as provided in the maven-plugin-testing-harness plug-in.

<?xml version="1.0" encoding="UTF-8"?>
<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>
	<prerequisites>
		<maven>3.0.3</maven>
	</prerequisites>
	
	<parent>
		<groupId>net.roboconf</groupId>
		<artifactId>parent</artifactId>
		<version>1.0-SNAPSHOT</version>
	</parent>
	
	<groupId>net.roboconf</groupId>
	<artifactId>roboconf-maven-plugin</artifactId>
	<version>1.0-SNAPSHOT</version>
	<name>Roboconf :: Maven Plug-in</name>
	<packaging>maven-plugin</packaging>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<maven.version>3.2.2</maven.version>
	</properties>
	
	<dependencies>
		<dependency>
			<groupId>org.apache.maven</groupId>
			<artifactId>maven-plugin-api</artifactId>
			<version>${maven.version}</version>
		</dependency>
		
		<dependency>
			<groupId>org.apache.maven.plugin-tools</groupId>
			<artifactId>maven-plugin-annotations</artifactId>
			<version>3.3</version>
			<scope>provided</scope>
		</dependency>
		
		<dependency>
			<groupId>net.roboconf</groupId>
			<artifactId>roboconf-core</artifactId>
			<version>${project.version}</version>
		</dependency>
		
		<!-- THIS is the important part -->
		<dependency>
			<groupId>org.apache.maven.plugin-testing</groupId>
			<artifactId>maven-plugin-testing-harness</artifactId>
			<version>3.2.0</version>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>org.apache.maven</groupId>
			<artifactId>maven-aether-provider</artifactId>
			<version>${maven.version}</version>
			<scope>test</scope>
		</dependency>
	
		<dependency>
			<groupId>org.apache.maven</groupId>
			<artifactId>maven-core</artifactId>
			<version>${maven.version}</version>
			<scope>test</scope>
		</dependency>
		
		<dependency>
			<groupId>org.apache.maven</groupId>
			<artifactId>maven-compat</artifactId>
			<version>${maven.version}</version>
			<scope>test</scope>
		</dependency>
		
		<dependency>
			<groupId>org.apache.maven</groupId>
			<artifactId>maven-model</artifactId>
			<version>${maven.version}</version>
			<scope>test</scope>
		</dependency>
		<!-- END of the important part -->
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-plugin-plugin</artifactId>
				<version>3.3</version>
				<configuration>
					<goalPrefix>roboconf</goalPrefix>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

I am using Maven 3.2.2. And in my test, I use JUnit 4.
So, the code to make it work is a little bit different than the one on Maven’s wiki. In particular, the getTestFile method does not exist anymore with the version 3.x of the maven-teting plug-ins.

So, here is a little snippet.

package net.roboconf.maven;

import java.io.File;

import junit.framework.Assert;

import org.apache.maven.plugin.testing.MojoRule;
import org.apache.maven.plugin.testing.resources.TestResources;
import org.junit.Rule;
import org.junit.Test;

/**
 * @author Vincent Zurczak - Linagora
 */
public class ValidateMojoTest {

	@Rule
	public MojoRule rule = new MojoRule();

	@Rule
	public TestResources resources = new TestResources();


	@Test
	public void testInvalidProject() throws Exception {

		File projectCopy = this.resources.getBasedir( "project--invalid" );
		File pom = new File( projectCopy, "pom.xml" );
		Assert.assertNotNull( pom );
		Assert.assertTrue( pom.exists());

		ValidateMojo mojo = (ValidateMojo) this.rule.lookupMojo( "validate", pom );
		Assert.assertNotNull( mojo );
		mojo.execute();
	}
}

And my file structure is the following one.

+ src/main/java
++ …
+ src/test/projects
++ project–invalid
+++ pom.xml
++ project–valid
+++ pom.xml
+++ …

All the Maven projects I use for tests are declared under src/test/projects.
I use a TestResources rule to copy this projet in my target directory and work on a copy. And I use a MojoRule rule to retrieve information and execute actions on my project through my plug-in.

For the record, the validate mojo I invoke in my unit test was associated with my Maven plug-in in the src/main/resources/META-INF/plexus/components.xml file. This is because I use a custom packaging for my project.

Eventually, here is the POM I have under the project–invalid directory.

<?xml version="1.0" encoding="UTF-8"?>
<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>
	<prerequisites>
		<maven>3.0.3</maven>
	</prerequisites>
	
	<groupId>net.roboconf.test</groupId>
	<artifactId>this-is-for-test-only</artifactId>
	<version>1.0-SNAPSHOT</version>
	<name>This is for Test ONLY</name>
	<packaging>roboconf-app</packaging>
	
	<build>
		<plugins>
			<plugin>
				<groupId>net.roboconf</groupId>
				<artifactId>roboconf-maven-plugin</artifactId>
				<version>${project.version}</version>
				<extensions>true</extensions>
				<configuration></configuration>
			</plugin>
		</plugins>
	</build>
</project>

Roboconf-maven-plugin is the plug-in under test.
That’s it. The original sources are (or will be soon) on GitHub.