我的插件mojo测试类利用maven-plugin-test-harness
构建完整的maven环境,包含所有pom配置,plexus容器上下文和repo访问。
以下所有内容都应该有效:
pom.xml
由于缺乏具体的工作示例,我一直在尝试使用我从SO收集的内容以及其他在线博客或文章的不同修补程序。
我目前正在努力让maven解决这些文物。虽然我从maven项目对象获得了依赖项列表,但工件列表是空的。
这是我通过剖析AbstractMojoTestCase
建立起来的。
我无法使用MojoRule
,因为JUnit5不再使用@Rules
。
另外,一些maven API调用已被弃用,但我找不到新的实现。我认为直到mvn4才会出现。请参阅下面的引用。
@BeforeEach
protected void setUp() throws Exception {
super.setUp();
cleanUp();
ClassLoader classLoader = getClass().getClassLoader();
URL url = classLoader.getResource(TEST_POM);
if (url == null) {
throw new MojoExecutionException(String.format(
"Cannot locate %s", TEST_POM));
}
File pom = new File(url.getFile());
//noinspection deprecation - wait on maven-plugin-testing-harness update
MavenSettingsBuilder mavenSettingsBuilder = (MavenSettingsBuilder)
getContainer().lookup(MavenSettingsBuilder.ROLE);
Settings settings = mavenSettingsBuilder.buildSettings();
MavenExecutionRequest request = new DefaultMavenExecutionRequest();
request.setPom(pom);
request.setLocalRepositoryPath(settings.getLocalRepository());
MavenExecutionRequestPopulator populator =
getContainer().lookup(MavenExecutionRequestPopulator.class);
populator.populateDefaults(request);
DefaultMaven maven = (DefaultMaven) getContainer().lookup(Maven.class);
DefaultRepositorySystemSession repoSession =
(DefaultRepositorySystemSession)
maven.newRepositorySession(request);
LocalRepository localRepository = new LocalRepository(
request.getLocalRepository().getBasedir());
SimpleLocalRepositoryManagerFactory factory =
new SimpleLocalRepositoryManagerFactory();
LocalRepositoryManager localRepositoryManager =
factory.newInstance(repoSession, localRepository);
repoSession.setLocalRepositoryManager(localRepositoryManager);
ProjectBuildingRequest buildingRequest =
request.getProjectBuildingRequest()
.setRepositorySession(repoSession)
.setResolveDependencies(true);
ProjectBuilder projectBuilder = lookup(ProjectBuilder.class);
MavenProject project =
projectBuilder.build(pom, buildingRequest).getProject();
//noinspection deprecation - wait on maven-plugin-testing-harness update
MavenSession session = new MavenSession(getContainer(), repoSession,
request, new DefaultMavenExecutionResult());
session.setCurrentProject(project);
session.setProjects(Collections.singletonList(project));
request.setSystemProperties(System.getProperties());
testMojo = (GenerateConfig) lookupConfiguredMojo(session,
newMojoExecution("configure"));
copyTestProjectResourcesToTarget(getContainer(), project, session);
}
[更新2017-07-27] :实际上现在解决了我的大部分问题。
现在只有几个小问题:
@Deprecated
,因此我假设有更好的方法(使用MavenSettingsBuilder.buildSettings()
) [更新2017-08-01] :测试现在需要访问一些属性文件,这些文件位于target/classes
目录中的实时环境中的类路径上。
逻辑上,它们是我的maven-plugin项目中的测试资源,因此我将它们包含在pom.xml
目录中的测试src/test/resources/my-test-project
所在的目录下。
这没用,所以我也试过src/test/resources/my-test-project/src/main/resources
,但这也不好。
我很难在测试期间确定插件的类路径上的确切内容,或者确定如何正确构造它。
[更新2017-08-02] :虽然我已经回答了我自己的问题(而不是扩展这个问题),但整个事情还没有完成,所以我没有标记这已经回答了。
只是为了记录,这些是所需的依赖项:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.0.0-M4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>4.12.0-M4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>3.5.0</version>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>3.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-testing</groupId>
<artifactId>maven-plugin-testing-harness</artifactId>
<version>3.3.0</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-container-default</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
<version>3.5.0</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-compat</artifactId>
<version>3.5.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.twdata.maven</groupId>
<artifactId>mojo-executor</artifactId>
<version>2.3.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
<scope>test</scope>
</dependency>
[更新2017-08-09]:
我必须添加更多功能,并发现测试很好,如果它想要解压缩的依赖项已经在本地repo中,但如果没有,它将无法获取它。
我现在需要确定如何指示maven从远程仓库获取依赖项。
我尝试启动依赖插件并在测试设置中调用resolve
,但它很糟糕,我认为必须有一种更简单的方法。
答案 0 :(得分:1)
我找到了从远程存储库中获取依赖项的解决方案。
使用像这样的maven内部,并从已弃用的类和重复功能的数量来判断,它给我的印象是maven v4会使这个多余。
使用此安装例程的一个小故障是它在maven项目目录中创建本地存储库目录树。这显然是不可取的,但需要进行一些调整才能解决。
@BeforeEach
public void setUp() throws Exception {
super.setUp();
ClassLoader classLoader = getClass().getClassLoader();
URL url = classLoader.getResource(TEST_POM);
if (url == null) {
throw new MojoExecutionException(String.format(
"Cannot locate %s", TEST_POM));
}
File pom = new File(url.getFile());
Settings settings = getMavenSettings();
if (settings.getLocalRepository() == null) {
settings.setLocalRepository(
org.apache.maven.repository.RepositorySystem
.defaultUserLocalRepository.getAbsolutePath());
}
MavenExecutionRequest request = new DefaultMavenExecutionRequest();
request.setPom(pom);
ArtifactRepository artifactRepository =
new org.apache.maven.artifact.repository.
DefaultArtifactRepository(
"id", settings.getLocalRepository(),
new DefaultRepositoryLayout());
request.setLocalRepository(artifactRepository);
MavenExecutionRequestPopulator populator =
getContainer().lookup(MavenExecutionRequestPopulator.class);
populator.populateFromSettings(request, settings);
DefaultMaven maven = (DefaultMaven)
getContainer().lookup(Maven.class);
DefaultRepositorySystemSession repositorySystemSession =
(DefaultRepositorySystemSession)
maven.newRepositorySession(request);
SimpleLocalRepositoryManagerFactory factory =
new SimpleLocalRepositoryManagerFactory();
LocalRepositoryManager localRepositoryManager =
factory.newInstance(repositorySystemSession,
new LocalRepository(settings.getLocalRepository()));
repositorySystemSession.setLocalRepositoryManager(
localRepositoryManager);
ProjectBuildingRequest buildingRequest =
request.getProjectBuildingRequest()
.setRepositorySession(repositorySystemSession)
.setResolveDependencies(true);
ProjectBuilder projectBuilder = lookup(ProjectBuilder.class);
ProjectBuildingResult projectBuildingResult =
projectBuilder.build(pom, buildingRequest);
MavenProject project = projectBuildingResult.getProject();
MavenSession session = new MavenSession(getContainer(),
repositorySystemSession, request,
new DefaultMavenExecutionResult());
session.setCurrentProject(project);
session.setProjects(Collections.singletonList(project));
request.setSystemProperties(System.getProperties());
testMojo = (GenerateConfig) lookupConfiguredMojo(session,
newMojoExecution("configure"));
testMojo.getLog().debug(String.format("localRepo = %s",
request.getLocalRepository()));
copyTestProjectResourcesToTarget(getContainer(), project, session);
resolveConfigurationFromRepo(repositorySystemSession, project);
}
private Settings getMavenSettings()
throws ComponentLookupException,
IOException,
XmlPullParserException {
org.apache.maven.settings.MavenSettingsBuilder mavenSettingsBuilder
= (org.apache.maven.settings.MavenSettingsBuilder)
getContainer().lookup(
org.apache.maven.settings.MavenSettingsBuilder.ROLE);
return mavenSettingsBuilder.buildSettings();
}
/**
* This is ugly but there seems to be no other way to accomplish it. The
* artifact that the mojo finds on its own will not resolve to a jar file
* on its own in the test harness. So we use aether to resolve it, by
* cloning the maven default artifact into an aether artifact and feeding
* an artifact request to the repo system obtained by the aether service
* locator.
*/
private void resolveConfigurationFromRepo(
DefaultRepositorySystemSession repositorySystemSession,
MavenProject project)
throws ArtifactResolutionException, MojoExecutionException {
org.apache.maven.artifact.Artifact defaultArtifact =
testMojo.getConfigArtifact();
Artifact artifact = new DefaultArtifact(
defaultArtifact.getGroupId(),
defaultArtifact.getArtifactId(),
null,
defaultArtifact.getType(),
defaultArtifact.getVersion());
List<RemoteRepository> remoteArtifactRepositories =
project.getRemoteProjectRepositories();
DefaultServiceLocator locator =
MavenRepositorySystemUtils.newServiceLocator();
locator.addService(RepositoryConnectorFactory.class,
BasicRepositoryConnectorFactory.class);
locator.addService(TransporterFactory.class,
FileTransporterFactory.class);
locator.addService(TransporterFactory.class,
HttpTransporterFactory.class);
RepositorySystem repositorySystem = locator.getService(
RepositorySystem.class);
ArtifactRequest artifactRequest = new ArtifactRequest();
artifactRequest.setArtifact(artifact);
artifactRequest.setRepositories(remoteArtifactRepositories);
ArtifactResult result = repositorySystem.resolveArtifact(
repositorySystemSession, artifactRequest);
defaultArtifact.setFile(result.getArtifact().getFile());
testMojo.getLog().debug( "Resolved artifact " + artifact + " to " +
result.getArtifact().getFile() + " from "
+ result.getRepository() );
}
/**
* Need manual copy of resources because only parts of the maven lifecycle
* happen automatically with this test harness.
*/
private void copyTestProjectResourcesToTarget(PlexusContainer container,
MavenProject project,
MavenSession session)
throws ComponentLookupException, MojoExecutionException {
Optional<Dependency> resourcesPluginDepOpt =
project.getDependencies().stream()
.filter(d -> Objects.equals(d.getArtifactId(),
MAVEN_RESOURCES_ARTIFACT_ID))
.findFirst();
// don't want to define the version here so we read it from what we have
if (!resourcesPluginDepOpt.isPresent()) {
throw new MojoExecutionException("Require " +
MAVEN_RESOURCES_ARTIFACT_ID);
}
Plugin resourcePlugin = MojoExecutor.plugin(
MojoExecutor.groupId(MAVEN_PLUGINS_GROUP_ID),
MojoExecutor.artifactId(MAVEN_RESOURCES_ARTIFACT_ID),
MojoExecutor.version(resourcesPluginDepOpt.get().getVersion()));
MojoExecutor.executeMojo(resourcePlugin,
MojoExecutor.goal("resources"),
MojoExecutor.configuration(),
MojoExecutor.executionEnvironment(
project, session,
container.lookup(BuildPluginManager.class)));
}
以下是使用的软件包,使用正确软件包中的类非常重要但很容易混淆:
import org.apache.maven.DefaultMaven;
import org.apache.maven.Maven;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout;
import org.apache.maven.execution.DefaultMavenExecutionRequest;
import org.apache.maven.execution.DefaultMavenExecutionResult;
import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.execution.MavenExecutionRequestPopulator;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.BuildPluginManager;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.testing.AbstractMojoTestCase;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.ProjectBuilder;
import org.apache.maven.project.ProjectBuildingRequest;
import org.apache.maven.project.ProjectBuildingResult;
import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
import org.apache.maven.settings.Settings;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory;
import org.eclipse.aether.impl.DefaultServiceLocator;
import org.eclipse.aether.internal.impl.SimpleLocalRepositoryManagerFactory;
import org.eclipse.aether.repository.LocalRepository;
import org.eclipse.aether.repository.LocalRepositoryManager;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.resolution.ArtifactRequest;
import org.eclipse.aether.resolution.ArtifactResolutionException;
import org.eclipse.aether.resolution.ArtifactResult;
import org.eclipse.aether.spi.connector.RepositoryConnectorFactory;
import org.eclipse.aether.spi.connector.transport.TransporterFactory;
import org.eclipse.aether.transport.file.FileTransporterFactory;
import org.eclipse.aether.transport.http.HttpTransporterFactory;
答案 1 :(得分:0)
MavenProject
的maven源代码中的一些注释表示
随着3.2.2发布期间的变化,MavenProject在构建之后更接近于不可变,并且从该类中删除了所有组件,并且前期构造完全由@ {ProjectBuilder}完成。仍然存在必须运行生命周期才能找到所有编译源根目录和资源目录的问题,但我希望在Maven 4.0版本(jvz)中处理这个问题。
我认为这整个maven插件集成测试的东西在那之前不会起作用......所以环顾四周,我找到了a great blog entry on invoking plugins。所以我直接调用了maven-resources-plugin
来让它复制它的意图。这就是copyTestProjectResourcesToTarget()
电话的作用。
private void copyTestProjectResourcesToTarget(PlexusContainer container,
MavenProject project,
MavenSession session)
throws ComponentLookupException, MojoExecutionException {
logger.fine("generateConfig dependencies: ");
project.getDependencies().forEach(d -> logger.fine(d.getArtifactId()));
Optional<Dependency> resourcesPluginDepOpt =
project.getDependencies().stream()
.filter(d -> Objects.equals(d.getArtifactId(),
MAVEN_RESOURCES_ARTIFACT_ID))
.findFirst();
// don't want to define the version here so we read it from what we have
if (!resourcesPluginDepOpt.isPresent()) {
throw new MojoExecutionException("Require " +
MAVEN_RESOURCES_ARTIFACT_ID);
}
Plugin resourcePlugin = MojoExecutor.plugin(
MojoExecutor.groupId(MAVEN_RESOURCES_GROUP_ID),
MojoExecutor.artifactId(MAVEN_RESOURCES_ARTIFACT_ID),
MojoExecutor.version(resourcesPluginDepOpt.get().getVersion()));
MojoExecutor.executeMojo(resourcePlugin,
MojoExecutor.goal("resources"),
MojoExecutor.configuration(),
MojoExecutor.executionEnvironment(
project, session,
container.lookup(BuildPluginManager.class)));
}
答案 2 :(得分:0)
不需要那么复杂。
<块引用>我不能使用 MojoRule,因为 JUnit5 不再使用 @Rules。
从 3.2 版开始,您不需要使用 @MojoRule,7 年前。只需按照以下三个步骤操作:
你的测试类应该扩展 AbstractMojoTestCase
在测试之前,调用 super.setUp()
查找您的魔力:
MyMojo myMojo = (MyMojo) super.lookupMojo("myGoal", "src/test/resources/its/my-test-mojo.pom.xml");
有了它,您就可以在没有开销的情况下使用 Junit 5、Mockito 等。