介绍

目前,Maven 仅支持开箱即用的单元测试。本文档旨在帮助 Maven 开发人员通过单元测试、集成测试和功能测试来测试插件。

测试风格:单元测试与功能/集成测试

单元测试通过模拟 Maven 环境的其余部分来尝试将 mojo 验证为一个独立的单元。mojo 单元测试不会尝试在真正的 Maven 构建的上下文中运行您的插件。单元测试旨在快速。

功能/集成测试通过在真实项目中启动真实的 Maven 实例,尝试在真实的 Maven 构建中使用 mojo。通常这需要您使用真实的 POM 文件构建特殊的虚拟 Maven 项目。这通常要求您已经将插件安装到本地存储库中,以便可以在真正的 Maven 构建中使用它。功能测试比单元测试运行得慢得多,但它们可以捕捉到单元测试可能无法捕捉到的错误。

一般的看法是,您的代码应该主要使用单元测试进行测试,但也应该进行一些功能测试。

单元测试

单独使用 JUnit

原则上,您可以像编写任何其他 JUnit 测试用例一样编写插件 Mojo 的单元测试,方法是编写一个extends TestCase.

但是,大多数 mojo 需要更多信息才能正常工作。例如,您可能需要注入对 MavenProject 的引用,以便您的 mojo 可以查询项目变量。

使用 PlexusTestCase

Mojo 变量是使用 Plexus 注入的,许多 Mojo 是为了利用 Plexus 容器的特定优势而编写的(通过执行生命周期或具有各种注入的依赖项)。

如果您只需要 Plexus 容器服务,则可以使用extends PlexusTestCaseTestCase 而不是编写您的类。

话虽如此,如果您需要将 Maven 对象注入到您的 mojo 中,您可能更喜欢使用 maven-plugin-testing-harness。

Maven 插件测试线束

maven -plugin-testing-harness明确用于测试org.apache.maven.reporting.AbstractMavenReport#execute()实现。

通常,您需要将maven-plugin-testing-harness其作为依赖项包含在内,并创建一个 *MojoTest(按照惯例)类,其中extends AbstractMojoTestCase.

...
  <dependencies>
    ...
    <dependency>
      <groupId>org.apache.maven.plugin-testing</groupId>
      <artifactId>maven-plugin-testing-harness</artifactId>
      <version>3.3.0</version>
      <scope>test</scope>
    </dependency>
    ...
  </dependencies>
...
public class YourMojoTest
    extends AbstractMojoTestCase
{
    /**
     * @see junit.framework.TestCase#setUp()
     */
    protected void setUp() throws Exception
    {
        // required for mojo lookups to work
        super.setUp();
    }

    /**
     * @throws Exception
     */
    public void testMojoGoal() throws Exception
    {
        File testPom = new File( getBasedir(),
          "src/test/resources/unit/basic-test/basic-test-plugin-config.xml" );

        YourMojo mojo = (YourMojo) lookupMojo( "yourGoal", testPom );

        assertNotNull( mojo );
    }
}

有关更多信息,请参阅Maven Plugin Harness Wiki

集成/功能测试

Maven 验证器

maven-verifier 测试使用 JUnit 或 TestNG 运行,并提供一个简单的类,允许您启动 Maven 并在其日志文件和构建工件上断言。它还提供了一个 ResourceExtractor,它将一个 Maven 项目从您的 src/test/resources 目录中提取到一个临时工作目录中,您可以用它来做一些棘手的事情。

Maven 本身使用 maven-verifier 来运行其核心集成测试。有关更多信息,请参阅创建 Maven 集成测试

public class TrivialMavenVerifierTest extends TestCase
{
    public void testMyPlugin()
        throws Exception
    {
        // Check in your dummy Maven project in /src/test/resources/...
        // The testdir is computed from the location of this
        // file.
        File testDir = ResourceExtractor.simpleExtractResources( getClass(), "/my-dummy-maven-project" );

        Verifier verifier;

        /*
         * We must first make sure that any artifact created
         * by this test has been removed from the local
         * repository. Failing to do this could cause
         * unstable test results. Fortunately, the verifier
         * makes it easy to do this.
         */
        verifier = new Verifier( testDir.getAbsolutePath() );
        verifier.deleteArtifact( "org.apache.maven.its.itsample", "parent", "1.0", "pom" );
        verifier.deleteArtifact( "org.apache.maven.its.itsample", "checkstyle-test", "1.0", "jar" );
        verifier.deleteArtifact( "org.apache.maven.its.itsample", "checkstyle-assembly", "1.0", "jar" );

        /*
         * The Command Line Options (CLI) are passed to the
         * verifier as a list. This is handy for things like
         * redefining the local repository if needed. In
         * this case, we use the -N flag so that Maven won't
         * recurse. We are only installing the parent pom to
         * the local repo here.
         */
        List cliOptions = new ArrayList();
        cliOptions.add( "-N" );
        verifier.executeGoal( "install" );

        /*
         * This is the simplest way to check a build
         * succeeded. It is also the simplest way to create
         * an IT test: make the build pass when the test
         * should pass, and make the build fail when the
         * test should fail. There are other methods
         * supported by the verifier. They can be seen here:
         * http://maven.apache.org/shared/maven-verifier/apidocs/index.html
         */
        verifier.verifyErrorFreeLog();

        /*
         * Reset the streams before executing the verifier
         * again.
         */
        verifier.resetStreams();

        /*
         * The verifier also supports beanshell scripts for
         * verification of more complex scenarios. There are
         * plenty of examples in the core-it tests here:
         * https://svn.apache.org/repos/asf/maven/core-integration-testing/trunk
         */
    }

注意:maven-verifier 和 maven-verifier-plugin 听起来很相似,但是完全不同的不相关的代码片段。maven-verifier-plugin 简单地验证文件系统上文件的存在/不存在。您可以将其用于功能测试,但您可能需要比 maven-verifier-plugin 提供的更多功能。

maven-invoker-plugin

您可以使用maven-invoker-plugin来调用 Maven 并提供一些 BeanShell/Groovy 测试。以这种方式编写的测试不能在 JUnit/TestNG 下运行;相反,它们由 Maven 本身运行。

您可以查看maven-install-plugin是如何编写集成测试的。

<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-invoker-plugin</artifactId>
        <version>1.10</version>
        <configuration>
          <projectsDirectory>src/it</projectsDirectory>
          <pomIncludes>
            <pomInclude>**/pom.xml</pomInclude>
          </pomIncludes>
          <postBuildHookScript>verify</postBuildHookScript>
        </configuration>
        <executions>
          <execution>
            <goals>
              <goal>run</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      ...
    </plugins>
  </build>
  ...
</project>