2017-07-21 77 views
0

我的插件mojo測試類利用maven-plugin-test-harness來構建具有所有pom配置,plexus容器上下文和repo訪問權限的完整maven環境。使用maven-plugin-testing-harness的困難時間mojo測試

下都應該實際工作:

  1. 測試將參考在插件項目的測試資源目錄測試pom.xml
  2. 的魔力將默認填充來自Mojo的註解
  3. 所有在測試pom中指定的配置被接受
  4. maven項目對象被初始化
  5. 所有從repo的依賴都可用
  6. 測試應該傳遞的IntelliJ IDE以及Maven的CI服務器上

由於缺乏具體的工作實例的我一直在嘗試使用的是什麼我從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年7月27日]:其實這個現在解決了大多數我的問題。

只有幾個小問題現在:

  1. 代碼搶settings.xml中被標記爲@Deprecated所以我假設有這樣做的更好的方法(使用MavenSettingsBuilder.buildSettings()
  2. 可能相當多的設置代碼是在本地maven中運行時發生的重複進程,但是需要在IntelliJ中使用JUnit運行。

[更新2017年8月1日]:現在測試需要訪問這將是在classpath在target/classes DIR現場環境中的某些屬性文件。

從邏輯上講,它們是我的maven-plugin項目中的測試資源,所以我將它們包含在與的src/test/resources/my-test-project dir相同的目錄下。

這並沒有工作,所以我也試過src/test/resources/my-test-project/src/main/resources但這也不好。

我很難在測試過程中確定插件類路徑究竟是什麼,或者正在研究如何使它正確構建。

[更新2017年8月2日]:雖然我已經回答了我的問題(而不是擴展這個問題),這整個事情還沒有結束,所以我不標示這是相當回答然而。

和公正的記錄,這些都是需要的相關性:

<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年8月9日]:

我必須添加一些更多的功能,發現測試如果它想解壓的依賴項已經存在於本地倉庫中,那麼它很好,但如果沒有,它將無法獲取它。

我現在需要確定如何指示maven從遠程倉庫獲取依賴關係。

我試着啓動依賴項插件,並在測試設置中調用resolve,但它死得不好,我認爲必須有一個更簡單的方法。

+0

首先,如果你認爲它是壞的記錄等等,爲什麼不幫助改善它呢?此外,你可以展示一個完整的工作例子,你的工作到目前爲止?最好是有一個github項目,我們可以看看它? – khmarbaise

+0

你是提交者嗎?我很樂意提供示例/改進javadoc,儘管這要取決於能否讓我的測試充分發揮功能。儘管我在這裏看到了有關Maven-plugin-test-harness的一些問題,但仍然不足以使該項目成爲「一言難盡」的應用程序。我也找不到多少谷歌搜索。我認爲那裏的網絡必須有討論 - 可能是一個開發人員列表郵件存檔的maven,沒有被搜索引擎索引? – Adam

+0

您可以通過以下網址搜索用戶郵件列表:http://www.mail-archive.com/[email protected]/。爲了幫助你的測試在github上運行一個完整的工作示例將會很有幫助...如果你願意貢獻於改進... – khmarbaise

回答

0

我找到了從遠程存儲庫獲取依賴關係的解決方案。

這樣的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; 
0

在Maven源代碼MavenProject一些評論說

隨着在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))); 
}