【问题标题】:Unit Testing GMavenPlus Groovy Mojos - project.basedir not being expanded单元测试 GMavenPlus Groovy Mojos - project.basedir 没有被扩展
【发布时间】:2015-06-15 15:31:06
【问题描述】:

我目前正在尝试使用 GMavenPlus(谢谢@Keegan!)和 Groovy 2.4.3 编写一个 Maven 插件。简而言之, 该插件解析 SQL DDL 的目录并从这些解析的 DDL 生成输出

Mojo 本身在构建、导入和运行在一个完整的消耗项目中时工作得很好。霍拉!

问题在于单元测试。当试图对这个 Mojo 进行单元测试时,像 ${project.basedir} 这样的 Maven POM 变量不会被 扩展,因此 mojo 失败,出现“找不到文件![${project.basedir}/src/test/resources/ddl]”之类的错误。作为 您可以从该错误消息中看到,${project.basedir} 是作为文字传递的,而不是被扩展的。

我目前正在使用 Maven 插件测试工具(具有固定依赖项,请参阅 this blog)、JUnit 4.12 和 AssertJ 3.0.0 作为我的测试堆栈。

有什么想法或特定技巧可以让 project.basedir 等内容在单元测试中扩展?

提前致谢!

有问题的单元测试:

import edge.SqlToScalaMojo
import org.junit.Before
import org.junit.Test

/**
 * Created by medge on 6/15/15.
 */
class SqlToScalaMojoTest extends BaseMojoTest<SqlToScalaMojo> {

    SqlToScalaMojo mojo

    @Before
    void setup() {
        mojo = getMojo("parse-ddls")
    }

    @Test
    void testMojoExecution() throws Exception {
        assertThat mojo isNotNull()

        mojo.execute()
    }
}

BaseMojoTest.groovy(实际上只是一个方便的基类):

import org.apache.maven.plugin.AbstractMojo
import org.apache.maven.plugin.testing.MojoRule
import org.junit.Rule

/**
 * Base Test class for Mojo tests. Extends {@link org.assertj.core.api.Assertions}
 *
 * If a type is given to this class then the result of #getMojo() does not have to be cast, reducing the amount of code
 * to be written in the unit tests themselves.
 *
 * Created by medge on 6/5/15.
 */
abstract class BaseMojoTest<T extends AbstractMojo> extends org.assertj.core.api.Assertions {

    /**
     * MojoRule used to lookup Mojos
     */
    @Rule public MojoRule rule = new MojoRule()

    /**
     * Get a configured mojo using the default pom file. Calls #getMojo(goal, getPom()) implicitly
     *
     * @param goal Goal to look up
     * @return T configured Mojo
     */
    T getMojo(String goal) {
        getMojo(goal, getPom())
    }

    /**
     * Get a configured mojo using the specified pom file
     *
     * @param goal Goal to look up
     * @param pom POM file to use when configuring Mojo
     * @return T configured Mojo
     */
    T getMojo(String goal, File pom) {
        T mojo = (T) rule.lookupMojo(goal, pom)

        mojo
    }

    /**
     * Default POM file if no custom path is given
     */
    String defaultPomPath = "src/test/resources/plugin-config.xml"

    /**
     * Return a File reference containing the default POM file
     *
     * @return File
     */
    File getPom() {
        getPom(defaultPomPath)
    }

    /**
     * Return a File reference containing the POM file found at the specified path. Implicitly asserts that the POM
     * exists using <code>assertFile</code>
     *
     * @param path Path to user-defined POM (overrides the default if provided)
     * @return File containing the specified POM.
     */
    File getPom(String path) {
        File _pom = getTestFile(path)

        // Implicitly assert POM exists
        assertFile(_pom)

        // Then return the POM file
        _pom
    }

    /**
     * Convenience method to assert that a file is valid
     *
     * @param file File to validate
     */
    static void assertFile(File file) {
        assertThat file isNotNull()
        assertThat file exists()
    }

    /**
     * Get the current project's base directory. From {@link org.codehaus.plexus.PlexusTestCase}
     *
     * @return Base directory path
     */
    static String getBaseDir() {
        final String path = System.getProperty( "basedir" );

        path ?: new File( "" ).getAbsolutePath();
    }

    /**
     * Return a test file from the src/test/resources directory. Assumes the base directory is src/test/resources so the
     * src/test/resources prefix can be omitted from the path if desired
     *
     * @param path File path
     * @return File
     */
    static File getTestFile(String path) {
        File testFile

        if(path.indexOf("src/test/resources/") > -1)
            testFile = getTestFile(getBaseDir(), path)
        else
            testFile = getTestFile(getBaseDir(), "src/test/resources/${path}")

        testFile
    }

    /**
     * Retrieve a test file from the given baseDir/path
     *
     * @param baseDir String base directory to look in
     * @param path String path to the file desired
     * @return File
     */
    static File getTestFile(String baseDir, String path) {
        new File(baseDir, path)
    }
}

Mojo 本身的主 POM 文件:

<?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>

    <groupId>edge</groupId>
    <artifactId>parser-mojo</artifactId>
    <version>0.0.3-SNAPSHOT</version>
    <packaging>maven-plugin</packaging>

    <properties>
        <groovy.version>2.4.3</groovy.version>
        <maven.version>3.3.3</maven.version>
        <junit.version>4.12</junit.version>
        <assertj.version>3.0.0</assertj.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy-all</artifactId>
            <version>${groovy.version}</version>
        </dependency>

        <!-- Test dependencies -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
        </dependency>
        <dependency>
            <groupId>org.assertj</groupId>
            <artifactId>assertj-core</artifactId>
            <version>${assertj.version}</version>
            <scope>test</scope>
        </dependency>


        <!-- Dependencies for Maven Mojos -->
        <dependency>
            <groupId>org.codehaus.plexus</groupId>
            <artifactId>plexus-utils</artifactId>
            <version>3.0.22</version>
        </dependency>
        <dependency>
            <groupId>org.apache.maven</groupId>
            <artifactId>maven-core</artifactId>
            <version>${maven.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.maven</groupId>
            <artifactId>maven-artifact</artifactId>
            <version>${maven.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.maven</groupId>
            <artifactId>maven-compat</artifactId>
            <version>${maven.version}</version>
        </dependency>
        <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.4</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>
            <type>jar</type>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.gmavenplus</groupId>
                <artifactId>gmavenplus-plugin</artifactId>
                <version>1.5</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>addSources</goal>
                            <goal>addTestSources</goal>
                            <goal>generateStubs</goal>
                            <goal>compile</goal>
                            <goal>testGenerateStubs</goal>
                            <goal>testCompile</goal>
                            <goal>removeStubs</goal>
                            <goal>removeTestStubs</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-plugin-plugin</artifactId>
                <version>3.4</version>
                <configuration>
                    <!-- see http://jira.codehaus.org/browse/MNG-5346 -->
                    <skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>
                </configuration>
                <executions>
                    <execution>
                        <id>generate-descriptor</id>
                        <goals>
                            <goal>descriptor</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>help-goal</id>
                        <goals>
                            <goal>helpmojo</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

在单元测试期间使用的测试 POM:

<project>
    <build>
        <plugins>
            <plugin>
                <groupId>edge</groupId>
                <artifactId>parser-mojo</artifactId>
                <version>0.0.3-SNAPSHOT</version>
                <configuration>
                    <template>${project.basedir}/src/test/resources/sample.template</template>
                    <inputDir>${project.basedir}/src/test/resources/ddl</inputDir>
                    <outputDir>${project.basedir}/src/test/resources/generated/</outputDir>
                </configuration>
                <executions>
                    <execution>
                        <phase>generate-resources</phase>
                        <goals>
                            <goal>parse-ddls</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

谢谢!

【问题讨论】:

    标签: maven groovy junit4 mojo gmaven


    【解决方案1】:

    找到了我自己问题的答案。在这里发帖以防其他人正在寻找相同的问题。

    首先,我遇到了 Mojo 本身关于 @Parameter 注释的另一个问题。以下:

    @Parameter(defaultValue = "${project.basedir}/src/main/resources")
    String inputDir
    

    会产生错误,因为 Groovy 编译器会在 Maven 之前提取字符串 "${project.basedir}/src/main/resources"。然后它将进行评估并将其转换为 GString。这会产生一个错误,因为注解需要一个 java.lang.String 但它会得到 java.lang.Object

    正如@Keegan 在another question 中指出的那样,解决方案是使用单引号而不是双引号:

    @Parameter(defaultValue = '${project.basedir}/src/main/resources')
    String inputDir
    

    Groovy 编译器不会评估字符串,Maven 将从那里获取

    接下来,在单元测试期间没有阅读关于 defaultValue 的原始问题。罪魁祸首是 BaseMojoTest.groovy 中的这个方法:

    T getMojo(String goal, File pom) {
        T mojo = (T) rule.lookupMojo(goal, pom)
    
        mojo
    }
    

    具体来说,这部分:

    rule.lookupMojo(goal, pom)
    

    MojoRule 上的lookupMojo 评估参数注释的默认值。它期望所有可能的参数都存在于测试 POM 文件中。快速深入了解 MojoRule 的源代码,我找到了修复它的方法:

    /**
     * Get a configured mojo using the specified pom file
     *
     * @param goal Goal to look up
     * @param pom POM file to use when configuring Mojo
     * @return T configured Mojo
     */
    T getMojo(String goal, File pom) {
        T mojo = (T) rule.lookupConfiguredMojo(getMavenProject(pom), goal)
    
        mojo
    }
    
    /**
     * Method to handle creating a MavenProject instance to create configured Mojos from
     * @param pom File to POM file containing Mojo config
     * @return MavenProject
     */
    MavenProject getMavenProject(File pom) {
        // create the MavenProject from the pom.xml file
        MavenExecutionRequest request = new DefaultMavenExecutionRequest()
        ProjectBuildingRequest configuration = request.getProjectBuildingRequest()
                .setRepositorySession(new DefaultRepositorySystemSession())
        MavenProject project = rule.lookup(ProjectBuilder.class).build(pom, configuration).getProject()
    
        project.basedir = new File(getBaseDir())
    
        // Implicit assert
        assertThat project isNotNull()
    
        // And return
        project
    }
    

    getMavenProject() 方法与MojoRule#readMavenProject() 中的代码略有不同,更改为引用 MojoRule 和我对 getBaseDir() 的定义。此方法生成的 Mojos 现在可以正确评估 @Parameter 的 defaultValue!我确实觉得getMavenProject() 可能会变得更有效率(而不是每次都创建一个 MavenProject),但现在就足够了。

    还有一件事

    您可能已经从getMavenProject() 方法中注意到了这一行:

    project.basedir = new File(getBaseDir())
    

    这个非常烦人的小技巧是必要的,因为在创建 MavenProject 对象时:

    MavenProject project = rule.lookup(ProjectBuilder.class).build(pom, configuration).getProject()
    

    ${project.basedir} 实际上成为单元测试中使用的测试 POM 文件 的 basedir。如果像我一样,你在 src/test/resources 中有一个测试 POM,那么 basedir 将变成类似于 /Users/medge/.../src/test/resources 的东西。所以,当@Parameter注解带有${project.basedir}展开时,如:

    @Parameter(defaultValue='${project.basedir}/src/main/resources')
    String inputDir
    

    从单元测试运行时,inputDir 将解析为/Users/medge/.../src/test/resources/src/main/resources

    微妙的事情会让你绊倒......

    希望这对其他人有帮助!

    【讨论】:

      【解决方案2】:

      我遇到了类似的问题,但我从配置中插入了路径并找到了不同的解决方案。

      我把测试项目pom改成这样:

      <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/xsd/maven-4.0.0.xsd">
          <modelVersion>4.0.0</modelVersion>
      
          <groupId>com.example.maven.plugin.MyPlugin</groupId>
          <artifactId>testProject</artifactId>
          <version>1.0-SNAPSHOT</version>
          <packaging>jar</packaging>
          <name>Test MyPlugin</name>
      
          <dependencies>
              <dependency>
                  <groupId>junit</groupId>
                  <artifactId>junit</artifactId>
                  <version>3.8.1</version>
                  <scope>test</scope>
              </dependency>
          </dependencies>
      
          <build>
              <plugins>
                  <plugin>
                      <artifactId>myPlugin-maven-plugin</artifactId>
                      <configuration>
                          <testDirectory>${basedir}\src\test\resources\testDir</testDirectory>
                      </configuration>
                  </plugin>
              </plugins>
          </build>
      </project>
      

      区别在于testDirectory。而

      &lt;testDirectory&gt;${project.basedir}\src\test\resources\testDir&lt;/testDirectory&gt;

      求值相同的字符串,this

      &lt;testDirectory&gt;${basedir}\src\test\resources\testDir&lt;/testDirectory&gt;

      评估文件系统C:\Development\projects\..\src\test\resources\testDir上的具体路径。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-13
        • 2010-10-21
        • 2013-05-08
        • 2019-08-18
        • 2020-07-12
        相关资源
        最近更新 更多