Testing Using Repositories

Note: This example improves the cookbook 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.

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.

Project dependencies for test

For real repository session you need to add resolver transport dependency in test scope to your pom.xml:

<dependency>
  <groupId>org.apache.maven.resolver</groupId>
  <artifactId>maven-resolver-connector-basic</artifactId>
  <version>${resloverVersion}</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.apache.maven.resolver</groupId>
  <artifactId>maven-resolver-transport-file</artifactId>
  <version>${resloverVersion}</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.apache.maven.resolver</groupId>
  <artifactId>maven-resolver-transport-http</artifactId>
  <version>${resloverVersion}</version>
  <scope>test</scope>
</dependency>

Example Mojo to test

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);
        }
    }
}

Unit 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.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;

/**
 * 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;

    @Inject
    private Log log;

    @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) {
        assertDoesNotThrow(mojo::execute);

        Mockito.verify(log, Mockito.times(1)).info(Mockito.contains("Resolved artifact to"));
    }

    @Nested
    class NestedTest {
        @Test
        @InjectMojo(goal = "simple-resolve")
        @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"));
        }
    }
}

Execute test

Calling mvn test will create <temp-directory>/org/apache/commons/commons-lang3/3.20.0/commons-lang3-3.20.0.jar file.