Maven Plugin(Mojo)開發中單元測試備忘

昨天準備就現在開發中的問題寫一個Maven插件,完成一些代碼的自動生成。在好幾年前寫過幾個插件,這次重新找開看后,發現原來的都很簡單,所以都沒有在開發期間的進行測試。考慮到這次寫的稍微復雜一些,如果每次修改到東西都要到目標項目中進行測試,那么效率太差了,所以準備先把插件開發中相關的單元測試搞定。

誰成想,這樣一下子發現了一個大坑。過程是這樣的:

  • 首先在Maven的官網上查找相關的文檔,發現原來codehaus這個組織已經關閉了,原來他們開發的很多插件都轉移到各處。
  • 官司網上的文檔對測試這塊講解很不清楚,如果按官網上的說明([How To Use Maven Plugin Testing Harness?]),過程中出現了幾個問題:
    • 首先在測試時會報MavenExecuteResult、MavenSession、MavenResporitory等各種對象不存在的問題
    • 官網的例子使用的是比較老的繼承AbstractMojoTestCase的方法,當解決了依賴的問題后,會發現如果在Mojo中使用類似${project.build.directory}等expression時,無法進行值的引用。
    • 這期間還發生了,在Mojo中使用@Parameter時在會出現

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-plugin-plugin:3.3:descriptor (default-descriptor) on project scaffold-maven-plugin: Execution default-descriptor of goal org.apache.maven.plugins:maven-plugin-plugin:3.3:descriptor failed: String index out of range: -1 -> [Help 1]

幸好有google在(真難以相像如果使用百度會是一個怎么樣的結果),經過長時間各種搜索發現了一篇最有用的文章Write Unit Tests for a Maven plug-in[感謝作者幫我解決了這個大問題],它解決了基本所有的問題,下面摘錄一些要點:

  • 上面問題中的第一個,也就是各種對象不存在的問題,主要原因是在測試中需要使用若干個關聯的jar包,但是在搜索中一個包一個包的添加,在運行中會出現版本不匹配的問題,各種糾結。應當按如下的方式配置想著的依賴:
    <?xml version="1.0" encoding="UTF-8"?>
    <project
    xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

        <modelVersion>4.0.0</modelVersion>
        <prerequisites>
            <maven>3.0.3</maven>
        </prerequisites>
     
        <parent>
            <groupId>net.roboconf</groupId>
            <artifactId>parent</artifactId>
            <version>1.0-SNAPSHOT</version>
        </parent>
    
         <groupId>net.roboconf</groupId>
         <artifactId>roboconf-maven-plugin</artifactId>
         <version>1.0-SNAPSHOT</version>
         <name>Roboconf :: Maven Plug-in</name>
         <packaging>maven-plugin</packaging>
     
         <properties>
             <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
             <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
             <maven.version>3.2.2</maven.version>
         </properties>
     
         <dependencies>
             <dependency>
                 <groupId>org.apache.maven</groupId>
                 <artifactId>maven-plugin-api</artifactId>
                 <version>${maven.version}</version>
             </dependency>
     
             <dependency>
                 <groupId>org.apache.maven.plugin-tools</groupId>
                 <artifactId>maven-plugin-annotations</artifactId>
                 <version>3.3</version>
                 <scope>provided</scope>
             </dependency>
     
             <dependency>
                 <groupId>net.roboconf</groupId>
                 <artifactId>roboconf-core</artifactId>
                 <version>${project.version}</version>
             </dependency>
     
             <!-- THIS is the important part -->
             <dependency>
                 <groupId>org.apache.maven.plugin-testing</groupId>
                 <artifactId>maven-plugin-testing-harness</artifactId>
                 <version>3.2.0</version>
                 <scope>test</scope>
             </dependency>
     
             <dependency>
                 <groupId>org.apache.maven</groupId>
                 <artifactId>maven-aether-provider</artifactId>
                 <version>${maven.version}</version>
                 <scope>test</scope>
             </dependency>
     
             <dependency>
                 <groupId>org.apache.maven</groupId>
                 <artifactId>maven-core</artifactId>
                 <version>${maven.version}</version>
                 <scope>test</scope>
             </dependency>
     
             <dependency>
                 <groupId>org.apache.maven</groupId>
                 <artifactId>maven-compat</artifactId>
                 <version>${maven.version}</version>
                 <scope>test</scope>
             </dependency>
     
             <dependency>
                 <groupId>org.apache.maven</groupId>
                 <artifactId>maven-model</artifactId>
                 <version>${maven.version}</version>
                 <scope>test</scope>
             </dependency>
             <!-- END of the important part -->
         </dependencies>
         <build>
             <plugins>
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-plugin-plugin</artifactId>
                     <version>3.3</version>
                     <configuration>
                         <goalPrefix>roboconf</goalPrefix>
                     </configuration>
                 </plugin>
             </plugins>
         </build>
     </project>
    

在這個實例中使用的版本是3.2.2,我自己經過測試,發現3.2.3也是可以使用的。另外,由于在Mojo中會使用如MavenProject這樣的對象,這樣的類差不多都在maven-core中,如果在compile時發現類找不到,可以將依賴中下面幾個scopetest的修改為provided

  • 在測試時,最早使用的是junit 3.8.1,所以使用的基類是AbstractMojoTestCase,這個類有很多問題,諸如在前面列舉的。所以,最好使用junit4以后的版本,按文章中的寫法,如下:
    package net.roboconf.maven;

    import java.io.File;
          
    import junit.framework.Assert;
    
    import org.apache.maven.plugin.testing.MojoRule;
    import org.apache.maven.plugin.testing.resources.TestResources;
    import org.junit.Rule;
    import org.junit.Test;
    
    public class ValidateMojoTest {
    
        @Rule
        public MojoRule rule = new MojoRule();
    
        @Rule
        public TestResources resources = new TestResources();
    
        @Test
        public void testInvalidProject() throws Exception {
            File projectCopy = this.resources.getBasedir( "project--invalid" );
            File pom = new File( projectCopy, "pom.xml" );
            Assert.assertNotNull( pom );
            Assert.assertTrue( pom.exists());
    
            ValidateMojo mojo = (ValidateMojo) this.rule.lookupMojo( "validate", pom );
            Assert.assertNotNull( mojo );
            mojo.execute();
        }
    }
    

由于 走過了一些彎路,所以上面的代碼是今天早上起床時才突然想到的,因為原來一起使用AbstractMojoTestCase,按那個思路在解決問題。今天早上突然回想起這個文章的這后半部分,說實在的,在剛看到文章時,this.rule.lookupMojo()這行其實出現了問題,所以就又回到原來的老路上。但是結合昨天查詢到的其它的文章:

只要把測試用例中的代碼修改為:
File projectCopy = this.resources.getBasedir("project-to-test");
File pom = new File(projectCopy, "pom.xml");
Assert.assertNotNull(pom);
Assert.assertTrue(pom.exists());

  MavenExecutionRequest executionRequest = new DefaultMavenExecutionRequest();
  ProjectBuildingRequest configuration = executionRequest.getProjectBuildingRequest()
              .setRepositorySession(new DefaultRepositorySystemSession());
  MavenProject project = rule.lookup(ProjectBuilder.class).build(pom, configuration).getProject();

  HibernateMojo mojo = (HibernateMojo) rule.lookupConfiguredMojo(project, "validate");
  Assert.assertNotNull(mojo);
  mojo.execute();

就可以解決在Mojo中無法識別${}expression的問題了。

  • 最后,項目的結構應當諸如:
    + src/main/java
    ++ …
    + src/test/projects
    ++ project–to-test
    +++ pom.xml
    ++ project–other-test
    +++ pom.xml
    +++ …
    其中,測試的pom.xml文件如下:
    <?xml version="1.0" encoding="UTF-8"?>
    <project
    xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

        <modelVersion>4.0.0</modelVersion>
        <prerequisites>
            <maven>3.0.3</maven>
        </prerequisites>
    
        <groupId>net.roboconf.test</groupId>
        <artifactId>this-is-for-test-only</artifactId>
        <version>1.0-SNAPSHOT</version>
        <name>This is for Test ONLY</name>
        <packaging>roboconf-app</packaging>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>net.roboconf</groupId>
                    <artifactId>roboconf-maven-plugin</artifactId>
                    <version>${project.version}</version>
                    <extensions>true</extensions>
                    <configuration></configuration>
                </plugin>
            </plugins>
        </build>
    </project>
    

最后,以下是幾個Mojo開發中有用的鏈接,備忘:

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,991評論 19 139
  • 我jdk版本是1.7.0_95,在網上查了一下1.7屬于java7maven3.3+版本都支持java7,所以我使...
    liangxifeng833閱讀 1,300評論 0 2
  • Maven編譯代碼的相關命令 第一、main目錄下的主代碼編寫完畢后,使用Maven進行編譯,在項目根目錄下運行命...
    加油小杜閱讀 1,259評論 0 2
  • 使用指導 如何添加外部依賴jar包 在Maven工程中添加依賴jar包,很簡單,只要在POM文件中引入對應的<de...
    靜默虛空閱讀 2,821評論 0 13
  • 寫作要求 堅持寫作第9天~主題寫作 閱讀應該是與寫作并行的,閱讀是寫作的基礎,所以,大家要完成的任務是讀一本自己曾...
    Albert陳凱閱讀 333評論 0 0