From 1e52d055eb8ad085f6cee6bd96651dac6686c413 Mon Sep 17 00:00:00 2001 From: Slawomir Jaranowski Date: Mon, 5 Jan 2026 22:31:29 +0100 Subject: [PATCH] Rewrite documentation and examples with new MojoTest usage As AbstractMojoTestCase is deprecated we can point documentation to new MojoTest and JUnit 5 --- .../maven/api/plugin/testing/InjectMojo.java | 19 +- .../api/plugin/testing/MojoExtension.java | 5 +- .../src/site/markdown/examples/artifact.md | 256 +++++----------- .../examples/complex-mojo-parameters.md | 241 +++++++--------- .../site/markdown/examples/multiproject.md | 138 ++------- .../site/markdown/getting-started/index.md | 273 ++++-------------- .../src/site/markdown/index.md | 15 +- .../src/site/markdown/migration-3.4.0.md | 147 ++++++++++ .../src/site/site.xml | 1 + .../plugin/testing/SimpleResolveMojoTest.java | 9 + 10 files changed, 460 insertions(+), 644 deletions(-) create mode 100644 maven-plugin-testing-harness/src/site/markdown/migration-3.4.0.md diff --git a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/InjectMojo.java b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/InjectMojo.java index 7fc4fdf..7b0acf4 100644 --- a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/InjectMojo.java +++ b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/InjectMojo.java @@ -30,7 +30,7 @@ * which Mojo should be instantiated and how it should be configured. * *

The annotation requires a {@code goal} attribute to specify which Mojo goal - * should be instantiated. Optionally, a custom {@code pom} file can be specified + * should be instantiated. Optionally, a custom {@code POM} file can be specified * to provide specific configuration for the test.

* *

Example usage on a test method:

@@ -85,7 +85,24 @@ @Target({ElementType.METHOD, ElementType.PARAMETER}) public @interface InjectMojo { + /** + * Specifies the goal of the Mojo to instantiate. + * This is a required attribute that maps to the Mojo's {@code @Mojo(name = "...")} + * annotation value. + * + * @return the goal name of the Mojo to test + */ String goal(); + /** + * Specifies an optional POM file to use for Mojo configuration. + * The path is relative to the test class location. + * + *

NOTE: only plugin configuration is taken from provided POM, all other tags are ignored.

+ * + *

If not specified, the default project configuration will be used.

+ * + * @return the path to a custom POM file, or an empty string to use defaults + */ String pom() default ""; } diff --git a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoExtension.java b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoExtension.java index 800f2a7..36726cb 100644 --- a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoExtension.java +++ b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoExtension.java @@ -138,7 +138,10 @@ * ** *

For custom POM configurations, you can specify a POM file using the {@link InjectMojo#pom()} - * attribute. The extension will merge this configuration with default test project settings.

* + * attribute. The extension will merge this configuration with default test project settings.

+ * + *

NOTE: only plugin configuration is taken from provided POM, all other tags are ignored.

+ * * * @see MojoTest * @see InjectMojo diff --git a/maven-plugin-testing-harness/src/site/markdown/examples/artifact.md b/maven-plugin-testing-harness/src/site/markdown/examples/artifact.md index 3168907..77f3cc4 100644 --- a/maven-plugin-testing-harness/src/site/markdown/examples/artifact.md +++ b/maven-plugin-testing-harness/src/site/markdown/examples/artifact.md @@ -23,203 +23,103 @@ date: February 2008 ### NOTE -`JUnit 3` based tests are deprecated since `3.4.0`. +**Note**: This example improves the [cookbook](../getting-started/index.html) to play with artifact handler. -Use JUnit 5 annotations, consult [javadocs](../apidocs/org/apache/maven/api/plugin/testing/package-summary.html) for examples. - **Note**: This example improves the [cookbook](../getting-started/index.html) to play with artifact handler. +Sometimes, your Mojo uses project artifact and ArtifactHandler mechanisms. For instance, you could need to filter on Java projects, i.e.: +```java +import javax.inject.Inject; - Sometimes, your Mojo uses project artifact and ArtifactHandler mechanisms. For instance, you could need to filter on Java projects, i.e.: +import org.apache.maven.project.MavenProject; - - -``` -public class MyMojo - extends AbstractMojo -{ +public class MyMojo extends AbstractMojo { /** * The Maven Project. */ - @Component - protected MavenProject project; - - public void execute() - throws MojoExecutionException - { - ... - - ArtifactHandler artifactHandler = project.getArtifact().getArtifactHandler(); - if ( "java".equals( artifactHandler.getLanguage() ) ) - { - ... - } - - ... - } -} -``` - -### Create Stubs - - - -``` -public class MyArtifactHandlerStub - extends DefaultArtifactHandler -{ - private String language; - - public String getLanguage() - { - if ( language == null ) - { - language = "java"; - } - - return language; - } + private final MavenProject project; - public void setLanguage( String language ) - { - this.language = language; + @Inject + MyMojo(MavenProject project) { + this.project = project; } -} -``` - - -``` -public class MyArtifactStub - extends ArtifactStub -{ - private String groupId; - - private String artifactId; - - private String version; - - private String packaging; - private VersionRange versionRange; + public void execute() throws MojoExecutionException { + // ... - private ArtifactHandler handler; - - /** - * @param groupId - * @param artifactId - * @param version - * @param packaging - */ - public ProjectInfoPluginArtifactStub( String groupId, String artifactId, - String version, String packaging ) - { - this.groupId = groupId; - this.artifactId = artifactId; - this.version = version; - this.packaging = packaging; - versionRange = VersionRange.createFromVersion( version ); - } - - /** {@inheritDoc} */ - public void setGroupId( String groupId ) - { - this.groupId = groupId; - } - - /** {@inheritDoc} */ - public String getGroupId() - { - return groupId; - } - - /** {@inheritDoc} */ - public void setArtifactId( String artifactId ) - { - this.artifactId = artifactId; - } - - /** {@inheritDoc} */ - public String getArtifactId() - { - return artifactId; - } - - /** {@inheritDoc} */ - public void setVersion( String version ) - { - this.version = version; - } - - /** {@inheritDoc} */ - public String getVersion() - { - return version; - } - - /** - * @param packaging - */ - public void setPackaging( String packaging ) - { - this.packaging = packaging; - } - - /** - * @return the packaging - */ - public String getPackaging() - { - return packaging; - } - - /** {@inheritDoc} */ - public VersionRange getVersionRange() - { - return versionRange; - } - - /** {@inheritDoc} */ - public void setVersionRange( VersionRange versionRange ) - { - this.versionRange = versionRange; - } - - /** {@inheritDoc} */ - public ArtifactHandler getArtifactHandler() - { - return handler; - } + ArtifactHandler artifactHandler = project.getArtifact().getArtifactHandler(); + if ("java".equals(artifactHandler.getLanguage())) { + //... + } - /** {@inheritDoc} */ - public void setArtifactHandler( ArtifactHandler handler ) - { - this.handler = handler; + // ... } } ``` +### Create a test + +```java +import org.apache.maven.api.di.Provides;import org.apache.maven.api.plugin.testing.InjectMojo; +import org.apache.maven.api.plugin.testing.MojoTest; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.handler.ArtifactHandler; +import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;import org.apache.maven.project.MavenProject; +import org.junit.jupiter.api.Nested; +import org.mockito.Mockito; + +@MojoTest +class ArtifactTest { + + @Inject + private MavenProject project; + + @Test + @InjectMojo(goal = "test") + void testUsingMockito(MyMojo mojo) { + // Mock ArtifactHandler + ArtifactHandler artifactHandler = Mockito.mock(ArtifactHandler.class); + Mockito.when(artifactHandler.getLanguage()).thenReturn("java"); + + // Mock Artifact + Artifact artifact = Mockito.mock(Artifact.class); + Mockito.when(artifact.getArtifactHandler()).thenReturn(artifactHandler); + + // Set the mocked Artifact to the default provided project + project.setArtifact(artifact); + + // Now you can test your Mojo logic that depends on the ArtifactHandler + mojo.execute(); + } + + @Nested + class NestedTest1 { + + @Inject + private ArtifactHandlerManager artifactHandlerManager; + + @Provides + MavenProject stubbedProject() { + MavenProject stubProject = new CustomMavenProject(); // your custom implementation + + ArtifactHandler stubArtifactHandler = new CustomArtifactHandler(); // your custom implementation + + // You can also get a real ArtifactHandler from the manager if needed + ArtifactHandler jarArtifactHandler = artifactHandlerManager.getArtifactHandler("jar"); + + Artifact stubArtifact = new CustomArtifact(stubArtifactHandler); // your custom implementation + + stubProject.setArtifact(stubArtifact); + return stubProject; + } -``` -public class MyProjectStub - extends MavenProjectStub -{ - /** - * Default constructor - */ - public MyProjectStub() - { - ... - - Artifact artifact = new MyArtifactStub( getGroupId(), getArtifactId(), - getVersion(), getPackaging() ); - artifact.setArtifactHandler( new MyArtifactHandlerStub() ); - setArtifact( artifact ); - - ... + @Test + @InjectMojo(goal = "test") + void testUsingStubbedProject (MyMojo mojo) { + // Use the stubbed project in your test + mojo.execute(); + } } - - ... } ``` - diff --git a/maven-plugin-testing-harness/src/site/markdown/examples/complex-mojo-parameters.md b/maven-plugin-testing-harness/src/site/markdown/examples/complex-mojo-parameters.md index fd76055..f979534 100644 --- a/maven-plugin-testing-harness/src/site/markdown/examples/complex-mojo-parameters.md +++ b/maven-plugin-testing-harness/src/site/markdown/examples/complex-mojo-parameters.md @@ -20,172 +20,141 @@ date: February 2008 ## Testing Complex Mojo Parameters -### NOTE +**Note**: This example improves the [cookbook](../getting-started/index.html) for testing complex Mojo parameters. -`JUnit 3` based tests are deprecated since `3.4.0`. +In real plugin development, you will use specific Maven objects like `MavenProject`, `MavenSession`, `MavenSettings` and so on. -Use JUnit 5 annotations, consult [javadocs](../apidocs/org/apache/maven/api/plugin/testing/package-summary.html) for examples. +### Provided mocks and stubs - **Note**: This example improves the [cookbook](../getting-started/index.html) for testing complex Mojo parameters. +The Maven Plugin Testing Harness provides mocks and stubs for the following Maven objects. +For creating mocks [Mockito](https://site.mockito.org/) framework is used. +#### MavenSession - In real plugin development, you will use specific Maven objects like `MavenProject`, `ArtifactRepository` or `MavenSettings`. You could use them by defining stubs. +There is provided a **mock** for `MavenSession` with basic configuration: +- `session#getUserProperties` and `session#getSystemProperties` empty `Properties` objects. +- `session#getCurrentProject` - a current prepared `MavenProject` +- `session#getLocalRepository` - equals to `request#getLocalRepository` +- `session#getRequest` returns a `spy` for `DefaultMavenExecutionRequest` with configuration + - `request#getStartTime` - celling time + - `request#getBaseDirectory()` - current base directory + - `request#getUserProperties`, `request#getSystemProperties` - copy of `MavenSession` properties + - `request#getLocalRepository`, `request#getLocalRepositoryPath` - point to local repository in `${basedir}/target/local-repo` + - `request#getRemoteRepositories`, `request#getPluginArtifactRepositories` - default remote repositories for Maven Central - Suppose that you have the following dependencies in the maven-my-plugin pom: +If `@MojoTest(realRepositorySession = true)` is used, then `session#getRepositorySession` returns +a real `RepositorySystemSession` configured with local repository in `${basedir}/target/local-repo` +and default remote repositories for Maven Central. +#### MavenProject +There is provided a **spy** for `MavenProject` with basic configuration: -``` - - ... - - - org.apache.maven - maven-artifact - 2.0.8 - - - org.apache.maven - maven-project - 2.0.8 - - ... - - -``` +- `project#getBasedir` - current base directory +- `project#getCompileSourceRoots` - `${basedir}/src/main/java` +- `project#getTestCompileSourceRoots` - `${basedir}/src/test/java` +- `project#getBuild` - a spy for `Build` with configuration + - `build#getDirectory` - `${basedir}/target` + - `build#getOutputDirectory` - `${basedir}/target/classes` + - `build#getTestOutputDirectory` - `${basedir}/target/test-classes` + - `build#getSourceDirectory` - `${basedir}/src/main/java` + - `build#getTestSourceDirectory` - `${basedir}/src/test/java` + - `build#getResources` - `${basedir}/src/main/resources` + - `build#getTestResources` - `${basedir}/src/test/resources` - You will add the following in the `MyMojo`: +If `@MojoTest(realRepositorySession = true)` is used, then `project#getRemote*Repositories` returns default remote repositories for Maven Central. +#### MojoExecution +There is provided a **spy** for `MojoExecution` with current plugin Mojo descriptor. -``` -public class MyMojo - extends AbstractMojo -{ - /** - * The Maven Project. - */ - @Parameter( defaultValue = "${project}", readonly = true ) - protected MavenProject project; - - /** - * Local Repository. - */ - @Parameter( defaultValue = "${localRepository}", readonly = true, required = true ) - protected ArtifactRepository localRepository; - - /** - * The Maven Settings. - */ - @Parameter( defaultValue = "${settings}", readonly = true ) - private Settings settings; - - ... -} -``` +#### Plugin logger -### Create Stubs +There is provided a **spy** for Slf4j wrapper of `org.apache.maven.plugin.logging.Log` interface. +You can verify log messages using Mockito verifications. +### Create custom Stubs for Maven objects - You need to create stub objects to run `MyMojoTest#testSomething()`. By convention, the package name should reflect the stubs, i.e. in our case `org.apache.maven.plugin.my.stubs`. +You can create your own stubs for Maven objects by define them in plugin test. +If you provide your own stub for a Maven object, then it will be used instead of the default provided one. +```java +import javax.inject.Inject; -``` -public class MyProjectStub - extends MavenProjectStub -{ - /** - * Default constructor - */ - public MyProjectStub() - { - MavenXpp3Reader pomReader = new MavenXpp3Reader(); - Model model; - try - { - model = pomReader.read( ReaderFactory.newXmlReader( new File( getBasedir(), "pom.xml" ) ) ); - setModel( model ); - } - catch ( Exception e ) - { - throw new RuntimeException( e ); - } +import org.apache.maven.api.di.Provides; +import org.apache.maven.api.plugin.testing.InjectMojo; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.project.MavenProject; +import org.apache.maven.settings.Settings; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +@MojoTest +class MyMojoTest { - setGroupId( model.getGroupId() ); - setArtifactId( model.getArtifactId() ); - setVersion( model.getVersion() ); - setName( model.getName() ); - setUrl( model.getUrl() ); - setPackaging( model.getPackaging() ); - - Build build = new Build(); - build.setFinalName( model.getArtifactId() ); - build.setDirectory( getBasedir() + "/target" ); - build.setSourceDirectory( getBasedir() + "/src/main/java" ); - build.setOutputDirectory( getBasedir() + "/target/classes" ); - build.setTestSourceDirectory( getBasedir() + "/src/test/java" ); - build.setTestOutputDirectory( getBasedir() + "/target/test-classes" ); - setBuild( build ); - - List compileSourceRoots = new ArrayList(); - compileSourceRoots.add( getBasedir() + "/src/main/java" ); - setCompileSourceRoots( compileSourceRoots ); - - List testCompileSourceRoots = new ArrayList(); - testCompileSourceRoots.add( getBasedir() + "/src/test/java" ); - setTestCompileSourceRoots( testCompileSourceRoots ); + // define your stub for MavenProject + @Provides + MavenProject stubProject() { + // return your stub implementation + return new MavenProjectStub(); } - /** {@inheritDoc} */ - public File getBasedir() - { - return new File( super.getBasedir() + "/src/test/resources/unit/project-to-test/" ); + @Provides + MavenSession stubSession() { + // return your stub implementation + return new MyMavenSessionStub(); } -} -``` + // inject the stubbed or default MavenSession + @Inject + private MavenSession session; -``` -public class SettingsStub - extends Settings -{ - /** {@inheritDoc} */ - public List getProxies() - { - return Collections.EMPTY_LIST; + @Inject + private Log log; + + @BeforeEach + + void setup() { + // customize injected stubs or mocks + Mockito.when(session.getSettings()).thenReturn(new MySettingsStub()); } -} -``` + @Test + @InjectMojo(goal = "my-goal") + void myMojoTest(MyMojo mojo) { + mojo.execute(); + // your verifications, eg + Mockito.verify(log).info("My info log message"); + } -### Configure `project-to-test` pom + // you can also group stubs in nested test classes + @Nested + class NestedTest { + // define your stub for MavenProject in nested class + @Provides + MavenProject stubProject() { + // return your stub implementation + return new MavenProjectStub2(); + } + @BeforeEach + void setup() { + // customize injected stubs or mocks for this nested class + Mockito.when(session.getSettings()).thenReturn(new MySettingsStub()); + } + @Test + @InjectMojo(goal = "my-goal") + void myMojoTest(MyMojo mojo) { + mojo.execute(); + // your verifications + } + } +} ``` - - ... - - - - maven-my-plugin - - - target/test-harness/project-to-test - - - ${localRepository} - - - - - - - - -``` - - diff --git a/maven-plugin-testing-harness/src/site/markdown/examples/multiproject.md b/maven-plugin-testing-harness/src/site/markdown/examples/multiproject.md index 7b76260..be42c91 100644 --- a/maven-plugin-testing-harness/src/site/markdown/examples/multiproject.md +++ b/maven-plugin-testing-harness/src/site/markdown/examples/multiproject.md @@ -22,134 +22,60 @@ date: February 2008 ### NOTE -`JUnit 3` based tests are deprecated since `3.4.0`. - -Use JUnit 5 annotations, consult [javadocs](../apidocs/org/apache/maven/api/plugin/testing/package-summary.html) for examples. - **Note**: This example improves the [cookbook](../getting-started/index.html) for multi-project testing. +Your Mojo should have `aggregator` parameter set to `true` - [Maven Plugin Tools Java Annotations](https://maven.apache.org/plugin-tools/maven-plugin-tools-annotations/index.html) - Your Mojo should have `@aggregator` parameter, i.e.: - - - - - with java annotations ([maven-plugin-plugin 3.x](/plugin-tools/)): - -``` -@Mojo( name = "touch", aggregator = true ) -public class MyMojo - extends AbstractMojo -{ +```java +@Mojo(name = "touch", aggregator = true) +public class MyMojo extends AbstractMojo { ... } ``` - - or with javadoc tags: - -``` -/** - * @goal touch - * @aggregator - */ -public class MyMojo - extends AbstractMojo -{ - ... -} -``` - +To test a Mojo in a multiproject area, you need to define several stubs, i.e. for the main test project and its modules. - To test a Mojo in a multiproject area, you need to define several stubs, i.e. for the main test project and its modules. +### Configure Mian project and create Stubs for the sub projects +```java +import org.apache.maven.api.plugin.testing.MojoTest; +import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager; +import org.mockito.Mockito; -### Create Stubs +@MojoTest +class AggregateTest { + @Inject + private MavenProject project; - Stub for the main test project: + @Inject + private MavenSession session; + @BeforeEach + void setup() { + // Configure the main project as execution root + project.setExecutionRoot(true); + MavenProject stub1 = new MavenProject(); + // configure stub1 as needed -``` -public class MyProjectStub - extends MavenProjectStub -{ - /** - * Default constructor - */ - public MyProjectStub() - { - ... - - setExecutionRoot( true ); - } + MavenProject stub2 = new MavenProject(); + // configure stub2 as needed - /** {@inheritDoc} */ - public MavenProject getExecutionProject() - { - return this; + // return all projects in the reactor - reactorProjects + Mockito.when(session.getProjects()).thenReturn(Arrays.asList(project, stub1, stub2)); } -} -``` - - Stubs for the subprojects: + @Test + @Basedir("/unit/aggregate-test") + @InjectMojo(goal = "aggregate") + void aggregate(AggregatorMojo mojo) throws Exception { + mojo.execute(); -``` -public class SubProject1Stub - extends MavenProjectStub -{ - /** - * Default constructor - */ - public SubProject1Stub() - { - ... + // Verify behavior across all projects } } ``` - - -``` -public class SubProject2Stub - extends MavenProjectStub -{ - /** - * Default constructor - */ - public SubProject2Stub() - { - ... - } -} -``` - - -### Configure `project-to-test` pom - - - -``` - - ... - - - - maven-my-plugin - - ... - - - - - - - - - - -``` - - diff --git a/maven-plugin-testing-harness/src/site/markdown/getting-started/index.md b/maven-plugin-testing-harness/src/site/markdown/getting-started/index.md index 2b943db..83506b5 100644 --- a/maven-plugin-testing-harness/src/site/markdown/getting-started/index.md +++ b/maven-plugin-testing-harness/src/site/markdown/getting-started/index.md @@ -20,105 +20,19 @@ date: 2008-08-27 ## Cookbook: How To Use Maven Plugin Testing Harness? - - This guide is intended as a reference for those developing Maven plugins, with self-contained references and solutions for common testing cases. - -### NOTE - -`AbstractMojoTestCase` is based on `JUnit 3` and is deprecated since `3.4.0`. - -Instead of it use `@MojoTest` and other JUnit 5 annotations, consult [javadocs](../apidocs/org/apache/maven/api/plugin/testing/package-summary.html) for examples. - -#### Migration receipts - -Replace - -```java -public class MyMojoTest extends AbstractMojoTestCase { -} -``` - -by - -```java -@MojoTest -class MyMojoTest { -} -``` - ---- - - Replace - -```java -MyMojo myMojo = (MyMojo) lookupMojo("goal", pom); -``` - -by - -```java -@InjectMojo(goal = "goal", pom = "src/test/resources/unit/project-to-test/pom.xml") -``` - ---- - -Replace - -```java -public void test() {} - MyCoponent myCoponent = lookup(MyCoponent.class); -} -``` - -by - -```java -@MojoTest -class MyMojoTest { - - @Inject - private MyCoponent myCoponent; -} -``` +This guide is intended as a reference for those developing Maven plugins, with self-contained references and solutions for common testing cases. ### Prerequisites +We assume that you have already created a plugin. In this cookbook, we make reference to `MyMojo` in `maven-my-plugin`. - We assume that you have already created a plugin. In this cookbook, we make reference to `MyMojo` in `maven-my-plugin` which is generated by the Maven Archetype Plugin, i.e.: - - -``` -mvn archetype:create \ - -DgroupId=org.apache.maven.plugin.my \ - -DartifactId=maven-my-plugin \ - -DarchetypeArtifactId=maven-archetype-mojo -``` - - The generated structure should be: - -``` -maven-my-plugin - |- pom.xml - +- src/ - +- main/ - +- java/ - +- org/ - +- apache/ - +- maven/ - +- plugin/ - +- my/ - |- MyMojo.java -``` - - -### Recipe +You cane reference the [Guide to Developing Java Plugins](http://maven.apache.org/guides/plugin/guide-java-plugin-development.html) to create your first plugin. +### Testing Maven Plugin #### Add `maven-plugin-testing-harness` dependency - - As usual, just add `maven-plugin-testing-harness` as following in your pom. Be sure to specify `test` scope. - +As usual, just add `maven-plugin-testing-harness` as following in your POM. Be sure to specify `test` scope. ``` @@ -129,144 +43,85 @@ maven-my-plugin maven-plugin-testing-harness test + + org.junit.jupiter + junit-jupiter-api + test + ... ... ``` - #### Create a `MyMojoTest` +Create a `MyMojoTest` (by convention) class in `src/test/java/org/example/maven/plugin/my` directory. - Create a `MyMojoTest` (by convention) class in `src/test/java/org/apache/maven/plugin/my` directory. This class should extend `AbstractMojoTestCase` from `maven-plugin-testing-harness`. +```java +import javax.inject.Inject; -``` -import org.apache.maven.plugin.testing.AbstractMojoTestCase; - -public class MyMojoTest - extends AbstractMojoTestCase -{ - /** {@inheritDoc} */ - protected void setUp() - throws Exception - { - // required - super.setUp(); +import org.apache.maven.api.plugin.testing.Basedir; +import org.apache.maven.api.plugin.testing.InjectMojo; +import org.apache.maven.api.plugin.testing.MojoParameter; +import org.apache.maven.api.plugin.testing.MojoTest; +import org.apache.maven.execution.MavenSession; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; - ... - } +@MojoTest +class MyMojoTest { - /** {@inheritDoc} */ - protected void tearDown() - throws Exception - { - // required - super.tearDown(); + @Inject + private MavenSession session; - ... + // optional setup before each test + @BeforeEach + void setUp() { + // properties added to the Maven session can by used in the plugin configuration + session.getUserProperties().setProperty("someProperty", "someValue"); } - /** - * @throws Exception if any - */ - public void testSomething() - throws Exception - { - File pom = getTestFile( "src/test/resources/unit/project-to-test/pom.xml" ); - assertNotNull( pom ); - assertTrue( pom.exists() ); - - MyMojo myMojo = (MyMojo) lookupMojo( "touch", pom ); - assertNotNull( myMojo ); + @Test + @InjectMojo(goal = "touch", pom = "src/test/resources/unit/project-to-test/pom.xml") + public void testSomething(MyMojo myMojo) throws Exception { myMojo.execute(); - ... } -} -``` - - In this case, `testSomething()` will test `MyMojo` against a Maven project called `project-to-test`. - - - **Note**: By convention, projects for unit testing your should be in the test resources directory. - - - Alternatively to extending `AbstractMojoTestCase` and when using Junit-4.10 ff., you may use a `MojoRule`, which just embeds an `AbstractMojoTestCase`. - - - When you do not need the functionality in every test method, you may use the `@WithoutMojo` annotation to skip rule executions. - - -``` - -import org.apache.maven.plugin.testing.MojoRule; -import org.apache.maven.plugin.testing.WithoutMojo; - -import org.junit.Rule; -import static org.junit.Assert.*; -import org.junit.Test; - -public class MyMojoTest -{ - @Rule - public MojoRule rule = new MojoRule() - { - @Override - protected void before() throws Throwable - { - } - - @Override - protected void after() - { - } - }; - - /** - * @throws Exception if any - */ @Test - public void testSomething() - throws Exception - { - File pom = rule.getTestFile( "src/test/resources/unit/project-to-test/pom.xml" ); - assertNotNull( pom ); - assertTrue( pom.exists() ); - - MyMojo myMojo = (MyMojo) rule.lookupMojo( "touch", pom ); - assertNotNull( myMojo ); + // you can use @Basedir pointing to test classpath resource + @Basedir("/unit/project-to-test") + // if you not provide 'pom' parameter, it will look for 'pom.xml' in basedir + @InjectMojo(goal = "touch", pom = "another-pom.xml") + public void testSomething2(MyMojo myMojo) throws Exception { myMojo.execute(); - ... } - /** Do not need the MojoRule. */ - @WithoutMojo @Test - public void testSomethingWhichDoesNotNeedTheMojoAndProbablyShouldBeExtractedIntoANewClassOfItsOwn() - { - ... + @InjectMojo(goal = "touch") + // you can provide simple parameters directly in the test method, without using a POM file + @MojoParameter(name = "parameter1Name", value = "parameter1Value") + @MojoParameter(name = "parameter2Name", value = "parameter2Value") + public void testSomething3(MyMojo myMojo) throws Exception { + myMojo.execute(); + ... } - } - - ``` -#### Configuring `project-to-test` pom - +**Note**: By convention, projects for unit testing your should be in the test resources directory. - Just create a pom as usual. The names for groupId and artifactId don't really matter since this project will not be deployed. +#### Configuring `project-to-test` POM + Just create a POM as usual. Only plugin configuration is processed by the testing harness. **All other parts of the POM are ignored.** - -``` +```xml + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 org.apache.maven.plugin.my.unit @@ -275,22 +130,18 @@ public class MyMojoTest jar Test MyMojo - - - junit - junit - 3.8.1 - test - - - maven-my-plugin - target/test-harness/project-to-test + ${basedir}/target/test-output + + value1 + + ${someProperty} + @@ -301,23 +152,17 @@ public class MyMojoTest #### Execute test - - As usual, just call: - +As usual, just call: ``` mvn test ``` - - ### Resources - - - 1 [Guide to Developing Java Plugins](http://maven.apache.org/guides/plugin/guide-java-plugin-development.html) - - 1 [Guide to Configuring Plugins](http://maven.apache.org/guides/mini/guide-configuring-plugins.html) +- [Guide to Developing Java Plugins](http://maven.apache.org/guides/plugin/guide-java-plugin-development.html) +- [Guide to Configuring Plugins](http://maven.apache.org/guides/mini/guide-configuring-plugins.html) +- [Project unit tests](https://github.com/apache/maven-plugin-testing/tree/maven-plugin-testing-3.x/maven-plugin-testing-harness/src/test) diff --git a/maven-plugin-testing-harness/src/site/markdown/index.md b/maven-plugin-testing-harness/src/site/markdown/index.md index 9de97c5..77af42b 100644 --- a/maven-plugin-testing-harness/src/site/markdown/index.md +++ b/maven-plugin-testing-harness/src/site/markdown/index.md @@ -20,8 +20,10 @@ date: February 2008 ## Maven Plugin Testing Harness +The Maven Plugin Testing Harness provides mechanisms to manage tests on Mojos, i.e. by pre-constructing the [Plexus](https://codehaus-plexus.github.io/plexus-containers/)/[Sisu](https://eclipse.dev/sisu/org.eclipse.sisu.plexus/) components, +providing stub objects for Maven functionality such as projects, and populating fields from an XML file that resembles the plugin configuration in the POM. -The Maven Plugin Testing Harness provides mechanisms to manage tests on Mojos, i.e. by pre-constructing the [Plexus](http://plexus.codehaus.org) components, providing stub objects for Maven functionality such as projects, and populating fields from an XML file that resembles the plugin configuration in the POM. +The Maven Plugin Testing Harness provides default stubs/mocks for most commonly used Maven objects, such as `MavenProject`, `MavenSession`, etc. The best way to start is to read the cookbook [How to use Maven Plugin Testing Harness](./getting-started/index.html). @@ -31,12 +33,14 @@ Since version `3.4.0`, the Maven Plugin Testing Harness has been migrated to use This change allows for more modern testing practices and improved integration with other tools. JUnit 5 extension `MojoExtension` and annotation `@MojoTest` have similar functionalities -as in [Maven 4](https://maven.apache.org/ref/4-LATEST/maven-impl-modules/maven-testing/apidocs/index.html) +as in [Maven 4](https://maven.apache.org/ref/4-LATEST/impl/maven-testing/apidocs/index.html) for easier migration of tests for Maven 4. Project still supports JUnit 3/4 `AbstractMojoTestCase` and JUnit 4 `MojoRule` tests for backward compatibility but new tests should be written using JUnit 5. +There are some [migration receipts](./migration-3.4.0.html) to help you migrate your existing tests. + Therefore, some project dependencies have been set as optional to avoid conflicts with existing JUnit 3/4 and JUnit 5 tests. Your project should depend on the following artifacts, if needed: @@ -48,17 +52,12 @@ Your project should depend on the following artifacts, if needed: ### Examples - The following examples shows how to use the Testing Harness in more advanced use cases: - +The following examples shows how to use the Testing Harness in more advanced use cases: - [Testing Complex Mojo Parameters](./examples/complex-mojo-parameters.html) - - [Testing Multiproject](./examples/multiproject.html) - - [Testing Using Repositories](./examples/repositories.html) - - [Testing Project Artifact](./examples/artifact.html) - - [Plugins testing summary](https://maven.apache.org/plugin-developers/plugin-testing.html) diff --git a/maven-plugin-testing-harness/src/site/markdown/migration-3.4.0.md b/maven-plugin-testing-harness/src/site/markdown/migration-3.4.0.md new file mode 100644 index 0000000..50d611d --- /dev/null +++ b/maven-plugin-testing-harness/src/site/markdown/migration-3.4.0.md @@ -0,0 +1,147 @@ +title: Migration to 3.4.0+ +author: Sławomir Jaranowski +date: January 2026 + + + + + + + + + + + + + + + + + +## Migration to 3.4.0+ + +Since version `3.4.0`, the Maven Plugin Testing Harness has been migrated to use JUnit 5 as the testing framework. +This change allows for more modern testing practices and improved integration with other tools. + +### Migration receipts + +Replace + +```java +public class MyMojoTest extends AbstractMojoTestCase { +} +``` + +by + +```java +@MojoTest +class MyMojoTest { +} +``` + +--- + +Replace + +```java +MyMojo myMojo = (MyMojo) lookupMojo("goal", pom); +``` + +by + +```java +@InjectMojo(goal = "goal", pom = "src/test/resources/unit/project-to-test/pom.xml") +``` + +--- + +Replace + +```java +public void test() {} + MyCoponent myCoponent = lookup(MyCoponent.class); +} +``` + +by + +```java +@MojoTest +class MyMojoTest { + + @Inject + private MyCoponent myCoponent; +} +``` + +Replace implementation of stubs in the configuration XML file from + +```xml + + 4.0.0 + exmaple.test + example + 1.0-SNAPSHOT + + + + org.exmaple.plugins + my-maven-plugin + + + + + + + + + + + + +``` + +by code in the test class: + +```java +import javax.inject.Inject; + +import java.util.Arrays; + +import org.apache.maven.api.di.Provides; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.project.MavenProject; +import org.apache.maven.settings.Settings; +import org.junit.jupiter.api.BeforeEach; +import org.mockito.Mockito; + +@MojoTest +class MyMojoTest { + + @Provides + MavenProject stubProject() { + // return your stub implementation + return new MavenProjectStub(); + } + + // default Mockito mock for MavenSession will be injected + @Inject + private MavenSession session; + + // project crated by stubProject() method will be injected + @Inject + private MavenProject project; + + @BeforeEach + void setup() { + MavenProject stub1 = new SubProject1Stub(); + MavenProject stub2 = new SubProject2Stub(); + + // reactorProjects + Mockito.when(session.getProjects()).thenReturn(Arrays.asList(project, stub1, stub2)); + + Mockito.when(session.getSettings()).thenReturn(new Settings()); + } +} +``` \ No newline at end of file diff --git a/maven-plugin-testing-harness/src/site/site.xml b/maven-plugin-testing-harness/src/site/site.xml index 8c6dc15..7c450bb 100644 --- a/maven-plugin-testing-harness/src/site/site.xml +++ b/maven-plugin-testing-harness/src/site/site.xml @@ -25,6 +25,7 @@ under the License. + diff --git a/maven-plugin-testing-harness/src/test/java/org/apache/maven/plugin/testing/SimpleResolveMojoTest.java b/maven-plugin-testing-harness/src/test/java/org/apache/maven/plugin/testing/SimpleResolveMojoTest.java index d5e9304..170af65 100644 --- a/maven-plugin-testing-harness/src/test/java/org/apache/maven/plugin/testing/SimpleResolveMojoTest.java +++ b/maven-plugin-testing-harness/src/test/java/org/apache/maven/plugin/testing/SimpleResolveMojoTest.java @@ -27,10 +27,12 @@ import org.apache.maven.api.plugin.testing.MojoParameter; import org.apache.maven.api.plugin.testing.MojoTest; import org.apache.maven.execution.MavenSession; +import org.apache.maven.plugin.logging.Log; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; +import org.mockito.Mockito; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; @@ -47,6 +49,9 @@ class SimpleResolveMojoTest { @Inject private MavenSession session; + @Inject + private Log log; + @BeforeEach void beforeEach() { // optionally customize the session to use a temporary local repository @@ -64,6 +69,8 @@ void beforeEach() { @MojoParameter(name = "artifact", value = "org.apache.commons:commons-lang3:3.20.0") void artifactShouldBeResolved(SimpleResolveMojo mojo) { assertDoesNotThrow(mojo::execute); + + Mockito.verify(log, Mockito.times(1)).info(Mockito.contains("Resolved artifact to")); } @Nested @@ -73,6 +80,8 @@ class NestedTest { @MojoParameter(name = "artifact", value = "org.apache.commons:commons-lang3:3.20.0") void artifactShouldBeResolved(SimpleResolveMojo mojo) { assertDoesNotThrow(mojo::execute); + + Mockito.verify(log, Mockito.times(1)).info(Mockito.contains("Resolved artifact to")); } } }