昨天準(zhǔn)備就現(xiàn)在開發(fā)中的問題寫一個(gè)Maven插件,完成一些代碼的自動生成。在好幾年前寫過幾個(gè)插件,這次重新找開看后,發(fā)現(xiàn)原來的都很簡單,所以都沒有在開發(fā)期間的進(jìn)行測試??紤]到這次寫的稍微復(fù)雜一些,如果每次修改到東西都要到目標(biāo)項(xiàng)目中進(jìn)行測試,那么效率太差了,所以準(zhǔn)備先把插件開發(fā)中相關(guān)的單元測試搞定。
誰成想,這樣一下子發(fā)現(xiàn)了一個(gè)大坑。過程是這樣的:
- 首先在Maven的官網(wǎng)上查找相關(guān)的文檔,發(fā)現(xiàn)原來codehaus這個(gè)組織已經(jīng)關(guān)閉了,原來他們開發(fā)的很多插件都轉(zhuǎn)移到各處。
- 官司網(wǎng)上的文檔對測試這塊講解很不清楚,如果按官網(wǎng)上的說明([How To Use Maven Plugin Testing Harness?]),過程中出現(xiàn)了幾個(gè)問題:
- 首先在測試時(shí)會報(bào)MavenExecuteResult、MavenSession、MavenResporitory等各種對象不存在的問題
- 官網(wǎng)的例子使用的是比較老的繼承AbstractMojoTestCase的方法,當(dāng)解決了依賴的問題后,會發(fā)現(xiàn)如果在Mojo中使用類似
${project.build.directory}
等expression時(shí),無法進(jìn)行值的引用。 - 這期間還發(fā)生了,在Mojo中使用
@Parameter
時(shí)在會出現(xiàn)
[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]
幸好有g(shù)oogle在(真難以相像如果使用百度會是一個(gè)怎么樣的結(jié)果),經(jīng)過長時(shí)間各種搜索發(fā)現(xiàn)了一篇最有用的文章Write Unit Tests for a Maven plug-in[感謝作者幫我解決了這個(gè)大問題],它解決了基本所有的問題,下面摘錄一些要點(diǎn):
-
上面問題中的第一個(gè),也就是各種對象不存在的問題,主要原因是在測試中需要使用若干個(gè)關(guān)聯(lián)的jar包,但是在搜索中一個(gè)包一個(gè)包的添加,在運(yùn)行中會出現(xiàn)版本不匹配的問題,各種糾結(jié)。應(yīng)當(dāng)按如下的方式配置想著的依賴:
<?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>
在這個(gè)實(shí)例中使用的版本是3.2.2,我自己經(jīng)過測試,發(fā)現(xiàn)3.2.3也是可以使用的。另外,由于在Mojo中會使用如MavenProject這樣的對象,這樣的類差不多都在maven-core中,如果在compile時(shí)發(fā)現(xiàn)類找不到,可以將依賴中下面幾個(gè)scope
為test
的修改為provided
。
-
在測試時(shí),最早使用的是junit 3.8.1,所以使用的基類是
AbstractMojoTestCase
,這個(gè)類有很多問題,諸如在前面列舉的。所以,最好使用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(); } }
由于 走過了一些彎路,所以上面的代碼是今天早上起床時(shí)才突然想到的,因?yàn)樵瓉硪黄鹗褂肁bstractMojoTestCase,按那個(gè)思路在解決問題。今天早上突然回想起這個(gè)文章的這后半部分,說實(shí)在的,在剛看到文章時(shí),this.rule.lookupMojo()
這行其實(shí)出現(xiàn)了問題,所以就又回到原來的老路上。但是結(jié)合昨天查詢到的其它的文章:
- Unit Testing GMavenPlus Groovy Mojos - project.basedir not being expanded
- Maven Plugin Harness - Maven - Apache Software Foundation
只要把測試用例中的代碼修改為:
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的問題了。
-
最后,項(xiàng)目的結(jié)構(gòu)應(yīng)當(dāng)諸如:
+ 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>
最后,以下是幾個(gè)Mojo開發(fā)中有用的鏈接,備忘:
- Maven – Guide to Developing Java Plugins
-
Maven – Mojo API Specification
再說明一點(diǎn),這兩個(gè)地址都是Maven官網(wǎng)的地址,在正常訪問Maven官網(wǎng)時(shí),有些頁面只能左邊的導(dǎo)航部分,正文顯示不出來。但是在F墻后顯示是正常的,這點(diǎn)要注意。