Maven本身并不是一个单元测试框架,Java中主流单元测试框架为JUnit和TestNG。Maven所做的只是在构建执行到特定生命周期阶段的时候,通过插件来执行JUnit或TestNG的测试用例。这一插件就是maven-surefire-plugin,作为测试运行期Test Runner,它能兼容Junit3、JUnit4、TestNG。
在maven中的default生命周期,其中的test阶段被定义为“使用单元测试框架运行测试”。而生命周期阶段需要绑定到某个插件的目标才能完成真正的工作,test阶段正是与maven-surefire-plugin的test目标相绑定,这是一个内置的绑定。
在默认情况下,maven-surefire-plugin的test目标会自动执行测试源码路径(默认为src/test/java/)下所有符合一组命名模式的测试类。模式分别为:
- **/Test*.java:任何子目录下所有命名以Test开头的Java类
- **/*Test.java:任何子目录下所有命名以Test结尾的Java类
- **/*TestCase.java:任何子目录下所有命名以TestCase结尾的Java类
只要将测试类按照以上模式命名,Maven就能自动运行它们,用户也就不需要定义测试集合TestSuite来聚合测试用例TestCase。需要注意,以Tests结尾的测试类不会自动执行。
如果有需要,也可以自己定义运行测试类的模式,maven-surefire-plugin还支持更高级的TestNG测试集合xml文件。另外,为了能够运行测试,Maven需要在项目中引入测试框架的依赖,比如添加JUnit测试范围依赖。
- 跳过测试
需要Maven跳过测试时,在命令行加入参数skipTests即可
$ mvn package -DskipTests也可以在POM中配置maven-surfire-plugin插件提供该属性。但是不推荐,如果需要长期跳过测试则不需要测试代码。
有时与用户不仅想跳过测试运行,还想临时跳过测试代码的编译,可以通过以下命令(虽然不推荐):
$ mvn package -Dmaven.test.skip=true
参数maven.test.skip同时控制了maven-compile-plugin和maven-surefire-plugin这两个插件的行为。所以如果要通过POM配置实现,POM配置如下:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.10</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
- 动态指定要运行的测试用例
反复运行单个测试用例是日常开发中很常见的行为。例如,项目代码中有一个失败的测试用例,开发人员就会再次运行这个测试以获得详细的错误报告。在修复该测试的过程中,开发人员也会反复运行它以确认修复代码的正确性。如果仅仅为一个失败的测试用例而反复运行所有测试,会造成不必要的开支,特别是当项目中测试比较多的时候。
maven-surefire-plugin提供了一个test参数让Maven用户能够在命令行指定要运行的测试用例。例如,如果只想运行account-captcha中的RandomGeneratorTest,执行以下命令:
$ mvn test -Dtest=RandomGeneratorTest这里test的参数值是测试用例的类名,这行命令效果是只运行RandomGeneratorTest这一个测试类。
另外,这个test的参数还支持其他赋值方式,例如:
$ mvn test-Dtest=Random*Test 用星号匹配多个字符
$ mvn test-Dtest=RandomGeneratorTest,AccountCaptchaServiceTetst 用逗号指定多个测试用例
也可以混合星号和逗号使用。
如果未匹配到任何测试类,就会报错且构建失败。根据错误提示可以加上 -DfailIfNoTests=false,告诉maven-surefire-plugin即使没有任何测试也不要报错
- 包含与排除测试用例
例如,有些项目的测试类以Tests结尾,这并不符合3种模式。但用户可以通过以下配置让maven自动运行这一类测试:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.10</version>
<configuration>
<includes>**/*Tests.java</includes>
</configuration>
</plugin>
如上,两个星号**用来匹配任意路径,一个星号*匹配除路径分割符外的0个或多个字符。
类似地,也可以用excludes元素排除一些符合默认命名模式的测试类,如下:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.10</version>
<configuration>
<excludes>
<exclude>**/*SkipTest</exclude>
</excludes>
</configuration>
</plugin>
- 测试报告
- 基本的测试报告
默认情况下,maven-surfire-plugin会在项目的target/surefire-reports目录下生成两种格式的测试报告:简单文本格式和与Junit兼容的XML格式
例如,account-captcha的target目录下txt文件为:
-------------------------------------------------------------------------------
Test set: com.juvenxu.mvnbook.account.email.AccountEmailServiceTest
-------------------------------------------------------------------------------
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.834 sec而xml包含的信息就丰富多了,具体可以参见JUnit的标准
- 测试覆盖率报告
测试覆盖率是衡量代码质量的一个重要指标。Cobertura是一个优秀的开源测试覆盖率统计工具,Maven通过cobertura-maven-plugin与之集成,用户可以使用简单的命令生成报告。命令如下:
$ mvn cobertura:cobertura
- 重用测试代码
在命令行运行mvn package的时候,Maven会将项目的主代码及资源文件打包,将其安装或部署到仓库之后,这些代码就能为他人所用,从而实现Maven项目级别的重用。默认的打包方式不会包含测试代码,因此在使用外部依赖的时候,其构件一般都不会包含测试代码。
然后,在项目内部重用某个模块的测试代码是很常见的需求。可能某个底层模块的测试代码中包含了一些常用的测试工具类,或者一些高质量的测试基类供继承。这时就需要通过maven-jar-plugin将测试类打包,如下:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.3.2</version>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
上面配置中,maven-jar-plugin有两个目标,分别是jar和test-jar,前者通过Maven的内置绑定在default生命周期的package阶段运行,其行为是对项目主代码进行打包。而后者并没有内置绑定,因此上述插件配置显示声明该目标来打包测试代码。通过查询该插件的具体信息可以了解到,test-jar的默认绑定声明周期阶段为package,因此当运行mvn clean package会看到两个目标都得以执行,分别打包了项目主代码和测试代码。
现在,就可以通过依赖声明使用测试包构件,注意在依赖中声明type元素为test-jar,所有测试包构件都使用特殊的test-jar打包类型。注意,同样要使用test依赖范围。
参考书籍:《Maven实战》第10章——许晓斌著