diff --git a/maven-plugin-testing-harness/pom.xml b/maven-plugin-testing-harness/pom.xml
index bb972f95..49420cdb 100644
--- a/maven-plugin-testing-harness/pom.xml
+++ b/maven-plugin-testing-harness/pom.xml
@@ -31,7 +31,8 @@ under the License.
The Maven Plugin Testing Harness provides mechanisms to manage tests on Mojo.
- 5.13.0
+ 3.9.12
+ 1.9.25
3.5.3
@@ -61,6 +62,12 @@ under the License.
${mavenVersion}
provided
+
+ org.apache.maven.plugin-tools
+ maven-plugin-annotations
+ ${version.maven-plugin-tools}
+ test
+
javax.inject
javax.inject
@@ -110,6 +117,27 @@ under the License.
true
+
+
+ org.apache.maven.resolver
+ maven-resolver-connector-basic
+ ${resloverVersion}
+ test
+
+
+ org.apache.maven.resolver
+ maven-resolver-transport-file
+ ${resloverVersion}
+ test
+
+
+ org.apache.maven.resolver
+ maven-resolver-transport-http
+ ${resloverVersion}
+ test
+
+
+
org.codehaus.plexus
plexus-testing
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 6aa59ffd..6853301c 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
@@ -35,6 +35,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
@@ -50,9 +51,14 @@
import com.google.inject.Binder;
import com.google.inject.Module;
import com.google.inject.internal.ProviderMethodsModule;
+import org.apache.maven.RepositoryUtils;
import org.apache.maven.api.di.Provides;
+import org.apache.maven.execution.DefaultMavenExecutionRequest;
+import org.apache.maven.execution.MavenExecutionRequest;
+import org.apache.maven.execution.MavenExecutionRequestPopulator;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.execution.scope.internal.MojoExecutionScope;
+import org.apache.maven.internal.aether.DefaultRepositorySystemSessionFactory;
import org.apache.maven.lifecycle.internal.MojoDescriptorCreator;
import org.apache.maven.plugin.Mojo;
import org.apache.maven.plugin.MojoExecution;
@@ -81,6 +87,7 @@
import org.codehaus.plexus.util.xml.XmlStreamReader;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
+import org.eclipse.aether.RepositorySystemSession;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.api.extension.ParameterResolutionException;
@@ -93,6 +100,7 @@
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mockingDetails;
+import static org.mockito.Mockito.spy;
/**
* JUnit Jupiter extension that provides support for testing Maven plugins (Mojos).
@@ -223,6 +231,9 @@ public void beforeEach(ExtensionContext context) throws Exception {
MojoExecution mojoExecution = addMock(plexusContainer, MojoExecution.class, this::mockMojoExecution);
MavenSession mavenSession = addMock(plexusContainer, MavenSession.class, this::mockMavenSession);
+ // prepare MavenExecutionRequest to be available in BeforeEach methods in test classes
+ createMavenExecutionRequest(context);
+
SessionScope sessionScope = plexusContainer.lookup(SessionScope.class);
sessionScope.enter();
sessionScope.seed(MavenSession.class, mavenSession);
@@ -388,6 +399,11 @@ protected Mojo lookupMojo(
ExtensionContext extensionContext, String[] coord, Xpp3Dom pluginConfiguration, PluginDescriptor descriptor)
throws Exception {
PlexusContainer plexusContainer = getContainer(extensionContext);
+
+ MavenExecutionRequest request = setupMavenExecutionRequest(extensionContext);
+ plexusContainer.lookup(MavenExecutionRequestPopulator.class).populateDefaults(request);
+ setupRepositorySession(extensionContext, request);
+
// pluginkey = groupId : artifactId : version : goal
Mojo mojo = plexusContainer.lookup(Mojo.class, coord[0] + ":" + coord[1] + ":" + coord[2] + ":" + coord[3]);
@@ -405,15 +421,18 @@ protected Mojo lookupMojo(
MojoExecution mojoExecution = plexusContainer.lookup(MojoExecution.class);
if (mockingDetails(session).isMock()) {
- lenient().when(session.getCurrentProject()).thenReturn(mavenProject);
+ lenient().doReturn(mavenProject).when(session).getCurrentProject();
}
if (mockingDetails(mavenProject).isMock()) {
- lenient().when(mavenProject.getBasedir()).thenReturn(new File(getTestBasedir(extensionContext)));
+ lenient()
+ .doReturn(new File(getTestBasedir(extensionContext)))
+ .when(mavenProject)
+ .getBasedir();
}
if (mojoDescriptor.isPresent() && mockingDetails(mojoExecution).isMock()) {
- lenient().when(mojoExecution.getMojoDescriptor()).thenReturn(mojoDescriptor.get());
+ lenient().doReturn(mojoDescriptor.get()).when(mojoExecution).getMojoDescriptor();
}
if (pluginConfiguration != null) {
@@ -445,6 +464,100 @@ protected Mojo lookupMojo(
return mojo;
}
+ private boolean isRealRepositorySessionNotRequired(ExtensionContext context) {
+ return !AnnotationSupport.findAnnotation(context.getTestClass(), MojoTest.class)
+ .map(MojoTest::realRepositorySession)
+ .orElse(false);
+ }
+
+ /**
+ * Create a MavenExecutionRequest if not already present in the MavenSession
+ */
+ private void createMavenExecutionRequest(ExtensionContext context) throws ComponentLookupException {
+ PlexusContainer container = getContainer(context);
+ MavenSession session = container.lookup(MavenSession.class);
+ MavenExecutionRequest request = session.getRequest();
+
+ if (request == null && mockingDetails(session).isMock()) {
+ lenient()
+ .doReturn(spy(new DefaultMavenExecutionRequest()))
+ .when(session)
+ .getRequest();
+ }
+ }
+
+ private MavenExecutionRequest setupMavenExecutionRequest(ExtensionContext context) throws ComponentLookupException {
+ PlexusContainer container = getContainer(context);
+ MavenSession session = container.lookup(MavenSession.class);
+ MavenExecutionRequest request = session.getRequest();
+
+ if (request == null) {
+ // user can provide own MavenSession instance without a request
+ request = new DefaultMavenExecutionRequest();
+ }
+
+ if (request.getStartTime() == null) {
+ request.setStartTime(new Date());
+ }
+
+ if (request.getUserProperties().isEmpty()) {
+ request.setUserProperties(session.getUserProperties());
+ }
+
+ if (request.getSystemProperties().isEmpty()) {
+ request.setSystemProperties(session.getSystemProperties());
+ }
+
+ // set a default local repository path if none is set
+ if (request.getLocalRepositoryPath() == null && request.getLocalRepository() == null) {
+ request.setLocalRepositoryPath(getTestBasedir(context) + "/target/local-repo");
+ }
+
+ if (request.getBaseDirectory() == null) {
+ request.setBaseDirectory(new File(getTestBasedir(context)));
+ }
+
+ return request;
+ }
+
+ private void setupRepositorySession(ExtensionContext context, MavenExecutionRequest request)
+ throws ComponentLookupException {
+
+ if (isRealRepositorySessionNotRequired(context)) {
+ return;
+ }
+
+ PlexusContainer container = getContainer(context);
+
+ MavenProject mavenProject = container.lookup(MavenProject.class);
+ if (mockingDetails(mavenProject).isMock()) {
+ lenient()
+ .doReturn(request.getRemoteRepositories())
+ .when(mavenProject)
+ .getRemoteArtifactRepositories();
+ lenient()
+ .doReturn(request.getPluginArtifactRepositories())
+ .when(mavenProject)
+ .getPluginArtifactRepositories();
+ lenient()
+ .doReturn(RepositoryUtils.toRepos(request.getRemoteRepositories()))
+ .when(mavenProject)
+ .getRemoteProjectRepositories();
+ lenient()
+ .doReturn(RepositoryUtils.toRepos(request.getPluginArtifactRepositories()))
+ .when(mavenProject)
+ .getRemotePluginRepositories();
+ }
+
+ RepositorySystemSession repositorySystemSession =
+ container.lookup(DefaultRepositorySystemSessionFactory.class).newRepositorySession(request);
+
+ MavenSession session = container.lookup(MavenSession.class);
+ if (mockingDetails(session).isMock()) {
+ lenient().doReturn(repositorySystemSession).when(session).getRepositorySession();
+ }
+ }
+
private Xpp3Dom finalizeConfig(Xpp3Dom config, MojoDescriptor mojoDescriptor) {
List children = new ArrayList<>();
if (mojoDescriptor != null && mojoDescriptor.getParameters() != null) {
diff --git a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoTest.java b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoTest.java
index 4aa9bbfc..063c1fa3 100644
--- a/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoTest.java
+++ b/maven-plugin-testing-harness/src/main/java/org/apache/maven/api/plugin/testing/MojoTest.java
@@ -83,4 +83,12 @@
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(MojoExtension.class)
@Target(ElementType.TYPE)
-public @interface MojoTest {}
+public @interface MojoTest {
+ /**
+ * Indicates whether to use a real repository session for the test.
+ *
+ * When set to {@code true}, the test will utilize a real repository session,
+ * allowing for artifact resolution and repository interactions.
+ */
+ boolean realRepositorySession() default false;
+}
diff --git a/maven-plugin-testing-harness/src/site/markdown/examples/repositories.md b/maven-plugin-testing-harness/src/site/markdown/examples/repositories.md
index 1098a4a8..05246e46 100644
--- a/maven-plugin-testing-harness/src/site/markdown/examples/repositories.md
+++ b/maven-plugin-testing-harness/src/site/markdown/examples/repositories.md
@@ -18,129 +18,38 @@ date: February 2008
-## Testing Using Repositories
-
-### 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 testing repositories.
-
-
- When developing a Maven plugin you often need to play with repositories. Suppose that the MyMojo needs to download artifacts into your local repository, i.e.:
-
-
-```
-public class MyMojo
- extends AbstractMojo
-{
- /**
- * Used for resolving artifacts
- */
- @Component
- private ArtifactResolver resolver;
-
- /**
- * Factory for creating artifact objects
- */
- @Component
- private ArtifactFactory factory;
-
- /**
- * Local Repository.
- */
- @Parameter( defaultValue = "${localRepository}", readonly = true, required = true )
- private ArtifactRepository localRepository;
-
- public void execute()
- throws MojoExecutionException
- {
- ...
-
- Artifact artifact = factory.createArtifact( "junit", "junit", "3.8.1", "compile", "jar" );
- try
- {
- resolver.resolve( artifact, project.getRemoteArtifactRepositories(), localRepository );
- }
- catch ( ArtifactResolutionException e )
- {
- throw new MojoExecutionException( "Unable to resolve artifact:" + artifact, e );
- }
- catch ( ArtifactNotFoundException e )
- {
- throw new MojoExecutionException( "Unable to find artifact:" + artifact, e );
- }
-
- ...
- }
-}
-```
-
-### Create Stubs
+## Testing Using Repositories
+**Note**: This example improves the [cookbook](../getting-started/index.html) for testing repositories.
+
+When developing a Maven plugin you often need to play with repositories.
+Suppose that the Mojo needs to download artifacts into your local repository.
- Stub for the test project:
+You need annotate unit test with `@MojoTest(realRepositorySession = true)` to enable real repository session.
+Then provided mock for `MavenSession` will have a real repository session with local repository configured.
+Mock for `MavenProject` will also have mocked methods `getRemote*Repositories`.
-```
-public class MyProjectStub
- extends MavenProjectStub
-{
- /**
- * Default constructor
- */
- public MyProjectStub()
- {
- ...
- }
+### Project dependencies for test
- /** {@inheritDoc} */
- public List getRemoteArtifactRepositories()
- {
- ArtifactRepository repository = new DefaultArtifactRepository( "central", "http://repo.maven.apache.org/maven2",
- new DefaultRepositoryLayout() );
+For real repository session you need to add resolver transport dependency in test scope to your `pom.xml`:
- return Collections.singletonList( repository );
- }
-}
-```
+
+### Example Mojo to test
-### Configure `project-to-test` pom
+
+### Unit test
+
-```
-
- ...
-
-
-
- maven-my-plugin
-
-
- ${basedir}/target/test-harness/project-to-test
-
-
- ${localRepository}
-
-
-
-
-
-
-
-```
#### Execute test
-
- Calling `mvn test` will create `$\{basedir\}/target/local-repo/junitjunit/3.8.1/junit-3.8.1.jar` file.
+ Calling `mvn test` will create `/org/apache/commons/commons-lang3/3.20.0/commons-lang3-3.20.0.jar` file.
diff --git a/maven-plugin-testing-harness/src/test/java/org/apache/maven/plugin/testing/SimpleResolveMojo.java b/maven-plugin-testing-harness/src/test/java/org/apache/maven/plugin/testing/SimpleResolveMojo.java
new file mode 100644
index 00000000..c9e2a16e
--- /dev/null
+++ b/maven-plugin-testing-harness/src/test/java/org/apache/maven/plugin/testing/SimpleResolveMojo.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.plugin.testing;
+
+// START SNIPPET: resolve-mojo
+import javax.inject.Inject;
+
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.project.MavenProject;
+import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.artifact.DefaultArtifact;
+import org.eclipse.aether.resolution.ArtifactRequest;
+import org.eclipse.aether.resolution.ArtifactResolutionException;
+import org.eclipse.aether.resolution.ArtifactResult;
+
+/**
+ * Simple mojo for resolving artifacts.
+ */
+@Mojo(name = "simple-resolve")
+public class SimpleResolveMojo extends AbstractMojo {
+
+ private final MavenProject project;
+ private final MavenSession session;
+ private final RepositorySystem repositorySystem;
+
+ @Parameter
+ private String artifact;
+
+ @Inject
+ SimpleResolveMojo(MavenProject project, MavenSession session, RepositorySystem repositorySystem) {
+ this.project = project;
+ this.session = session;
+ this.repositorySystem = repositorySystem;
+ }
+
+ @Override
+ public void execute() throws MojoExecutionException {
+
+ ArtifactRequest request =
+ new ArtifactRequest(new DefaultArtifact(artifact), project.getRemoteProjectRepositories(), null);
+
+ try {
+ ArtifactResult result = repositorySystem.resolveArtifact(session.getRepositorySession(), request);
+ getLog().info("Resolved artifact to " + result.getArtifact().getFile());
+ } catch (ArtifactResolutionException e) {
+ throw new MojoExecutionException("Failed to resolve artifact", e);
+ }
+ }
+}
+// END SNIPPET: resolve-mojo
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
new file mode 100644
index 00000000..9f697579
--- /dev/null
+++ b/maven-plugin-testing-harness/src/test/java/org/apache/maven/plugin/testing/SimpleResolveMojoTest.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.plugin.testing;
+
+// START SNIPPET: resolve-mojo-test
+import javax.inject.Inject;
+
+import java.io.File;
+
+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.apache.maven.plugin.MojoExecutionException;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+/**
+ * Test class for simple artifact resolution mojo with repository system injection.
+ */
+@MojoTest(realRepositorySession = true)
+class SimpleResolveMojoTest {
+
+ @TempDir
+ private File tempDir;
+
+ // inject the Maven session to customize it for tests
+ @Inject
+ private MavenSession session;
+
+ @BeforeEach
+ void beforeEach() {
+ // optionally customize the session to use a temporary local repository
+ // default would be basedir/target/local-repo
+ session.getRequest().setLocalRepositoryPath(tempDir);
+
+ // other customizations can be done here, if needed
+ // session.getRequest().setRemoteRepositories(customRemoteRepositories());
+ // session.getRequest().setOffline(true);
+ // session.getUserProperties().setProperty("maven.repo.local.tail", "your local repository");
+ }
+
+ @Test
+ @InjectMojo(goal = "simple-resolve")
+ @MojoParameter(name = "artifact", value = "org.apache.commons:commons-lang3:3.20.0")
+ void artifactShouldBeResolved(SimpleResolveMojo mojo) throws MojoExecutionException {
+ assertDoesNotThrow(mojo::execute);
+ }
+}
+// END SNIPPET: resolve-mojo-test
diff --git a/maven-plugin-testing-harness/src/test/resources/META-INF/maven/plugin.xml b/maven-plugin-testing-harness/src/test/resources/META-INF/maven/plugin.xml
index 7a17964c..c8d4ca8c 100644
--- a/maven-plugin-testing-harness/src/test/resources/META-INF/maven/plugin.xml
+++ b/maven-plugin-testing-harness/src/test/resources/META-INF/maven/plugin.xml
@@ -93,6 +93,28 @@ under the License.
once-per-session
false
+
+ simple-resolve
+ false
+ true
+ false
+ false
+ true
+ true
+ org.apache.maven.plugin.testing.SimpleResolveMojo
+ java
+ per-lookup
+ once-per-session
+ false
+
+
+ artifact
+ java.lang.String
+ true
+ true
+
+
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index fa47ba8d..9979f2e6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -64,7 +64,6 @@ under the License.
- 3.9.12
plugin-testing-archives/LATEST
8
2025-10-28T21:36:24Z