vlambda博客
学习文章列表

读书笔记《gradle-essentials》使用Gradle进行测试和报告

Chapter 7. Testing and Reporting with Gradle

在本章中,我们将介绍四个不同的主题:使用 TestNG 进行测试、集成测试、使用 JaCoCo 进行代码覆盖以及使用 Sonar 进行代码分析。在 第 2 章构建 Java 项目,我们已经讨论过使用 JUnit 进行单元测试。在本章中,我们将介绍另一个广泛使用的测试工具 TestNG。代码覆盖率和代码质量是 测试驱动开发 中的另外两个重要方面(TDD )。在当今的敏捷开发过程中,开发人员需要对他们开发的代码进行持续的反馈。代码质量工具帮助我们实现这一目标。通常,这些工具与 Continuous IntegrationCI) 系统,以便每天创建这些报告(甚至可能在每次提交之后),在不同团队之间共享,甚至保留以供将来分析.在本章中,我们将只关注不同工具的 Gradle 方面。我们将主要介绍支持这些功能的不同 Gradle 插件。

Testing with TestNG


使用 TestNG 类似于我们在 第 2 章中讨论的 JUnit 集成构建 Java 项目。第一步是创建带有 TestNG 依赖项的构建文件并配置测试闭包。以下构建文件将 TestNG 库添加为 testCompile 依赖项,在测试闭包中,我们添加了一个 testng.xml 文件执行测试用例。在本节中,我们将简要讨论 testng.xml 的使用:

apply plugin:'java'

repositories {
  mavenCentral()
}

dependencies {
  testCompile 'org.testng:testng:6.8.21'
}

test {
  ignoreFailures = true
  useTestNG(){
    suites("src/test/resources/testng.xml")
  }
}

Note

但是,您可以在 TestNG 配置的信息.html" target="_blank">http://testng.org/doc/documentation-main.html。

在我们的示例中,我们 创建了三个测试用例,分别命名为 verifyMapSizeverifyMapNotNulladdEvenNumbers。这些测试用例分为 SmokeIntegration 测试用例。如果执行 Gradle 测试命令,三个测试用例都将被执行,测试报告将在 build/reports/tests 目录中创建。报告的外观与我们之前看到的 JUnit 报告类似。实际的 TestNG 报告是在项目主目录的 test-output/ 目录中创建的。 JUnit 和 TestNG 都生成自己不同的报告格式,但 Gradle 将它们调和为标准外观:

package com.packtpub.ge.ch7;

import java.util.HashMap;

import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

public class HashTest {
  
  private HashMap<Integer,String> hm;
  
  @BeforeClass(alwaysRun = true)
  public void setup(){
    hm = new HashMap<Integer, String>();
  }
  
  @AfterMethod(alwaysRun = true)
  public void cleantask(){
    hm.clear();
  }
  
  @Test(groups = "Smoke")
  public void verifyMapSize(){
    Assert.assertEquals(hm.size(), 0);
    hm.put(1, "first");
    hm.put(2, "second");
    hm.put(3, "third");
    Assert.assertEquals(hm.size(), 3);
  }
  
  @Test(groups = "Smoke")
  public void verifyMapNotNull(){
    Assert.assertNotNull(hm);
    
  }
  
  @Test(groups = "Integration")
  public void addEvenNumbers(){
    hm.put(2, "second");
    hm.put(4, "fourth");
    Assert.assertEquals(hm.size(), 2);
  }

  
}

可以从命令行、Ant 文件、Gradle 脚本、Eclipse 插件或 TestNG 测试套件文件执行 TestNG 测试用例。 TestNG 套件文件为测试执行提供了灵活的机制控制。在测试套件文件中,您可以定义测试类、测试、测试组名称、侦听器信息等。

我们在 src/test/resource 文件夹中创建了一个示例 testng.xml 文件。该文件包含一些重要信息。 listener configuration 创建报告格式,一个测试组声明Smoke,和一个名为 com.packtpub.ge.ch7.HashTest 的测试类。

Gradle 不会强制你将 testng.xml 放入 src/test/resources,我们只是这样做作为保持组织有序的一种方式:

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="Suite1" verbose="1" >
  <listeners>
    <listener class-name="org.testng.reporters.EmailableReporter" />
  </listeners>
  <test name="Smoke Test">
  <groups>
    <run>
      <exclude name="Integration" />
      <include name="Smoke" />
    </run>
  </groups>
  <classes>
    <class name="com.packtpub.ge.ch7.HashTest">
    </class>
  </classes>
  </test>
</suite>

由于我们只包含了标记为Smoke的测试用例,TestNG只调用了两个测试用例, verifyMapNotNulladdEvenNumbers,当我们执行 gradle test 命令时。下图是在<Project_Home>/test-output/目录下创建的TestNG报告:

读书笔记《gradle-essentials》使用Gradle进行测试和报告

图 7.1

Integration testing


单元测试是软件开发生命周期中的关键步骤之一。这是验证代码质量的首批检查之一。大多数基本功能都可以使用单元测试用例进行测试。它们速度快,执行时间短。我们讨论了 JUnit 框架和 TestNG 框架来对代码进行单元测试。质量检查过程的下一步是集成测试。根据单元测试的一般定义,您将代码分成小单元并独立测试它们,这在您独立开发代码时很好。一旦您提交代码并将代码与其他开发人员集成,您需要另一个级别的测试,这称为集成测试。它验证不同组件之间的通信是否按预期协同工作。您的测试报告可能会在单元测试中给出 100% 的成功结果,但是除非并且直到您执行集成测试,否则您无法确保软件作为一个整体的功能。

我们已经看到了 Gradle 对单元测试的支持,以及 Gradle 如何提供约定以在不同的目录结构和任务中编写测试类来执行测试用例。如果我们按照它提供的约定来讨论,Gradle 不会区分单元测试和集成测试。要在 Gradle 中启用集成测试和单元测试,您需要自定义 Gradle 以启用两者。考虑项目源代码的以下层次结构:

C:.
└───IntegrationSample
   └───src
       ├───main
       │   └───java
       └───test
           └───java

这是您为源代码和测试代码创建的标准文件夹结构。您创建 src/test/java 来存储您的单元测试用例。现在,如果你想在你的项目中添加集成测试用例,你可以将集成测试用例合并到同一个目录结构中;但是,这不是一个好的设计——因为您可能希望每次构建项目时都执行单元测试用例,并且可能希望每两周或每周执行一次集成测试——因为它可能会花费更多时间,具体取决于项目的复杂性和尺寸。因此,我们建议您为集成测试用例创建一个单独的目录结构 src/integrationTest/java,而不是将集成测试合并到单元测试用例的目录结构中,您可以在 Gradle 构建脚本中进行相同的配置。

以下将是存储集成测试用例的更新目录结构:

C:.
└───IntegrationSample
   └───src
       ├───integrationTest
       │   └───java
       ├───main
       │   └───java
       └───test
           └───java

一旦你创建了目录结构,你需要在你的Gradle构建脚本中配置它。更新后的构建脚本如下:

apply plugin: 'java'
sourceSets {
   integrationTest {
       java.srcDir file('src/integrationTest/java')
       resources.srcDir file('src/integrationTest/resources') // to add the resources
   }
}

task runIntegrationTest(type: Test) {
   testClassesDir = sourceSets.integrationTest.output.classesDir
   classpath = sourceSets.integrationTest.runtimeClasspath
}

在这里,我们添加了一个额外的配置,integrationTest,用于添加集成测试用例。为了执行集成测试,我们还定义了一个任务 runIntegrationTest,它的类型是 Test 并配置了 testClassesDir 和类路径属性。一旦我们将额外的 sourceSets 添加到构建脚本中,java 插件会自动将两个新的依赖配置添加到您的构建脚本 integrationTestCompileintegrationTestRuntime

执行以下命令检查当前依赖项:

> gradle dependencies
------------------------------------------------------------
Root project
------------------------------------------------------------
……...
compile - Compile classpath for source set 'main'.
No dependencies
integrationTestCompile - Compile classpath for source set 'integration test'.
No dependencies
integrationTestRuntime - Runtime classpath for source set 'integration test'.
No dependencies
……….
BUILD SUCCESSFUL
Total time: 3.34 secs

此处,integrationTestCompile 可用于配置编译 测试用例和integrationTestRuntime 可用于配置执行测试用例所需的依赖项。如您所见,没有为集成测试用例显式配置依赖项。您可以在依赖项关闭下配置它们:

dependencies {
// other configuration dependencies 
integrationTestCompile 'org.hibernate:hibernate:3.2.3.ga'
}

我们不想在每次构建项目时都执行集成测试。因此,要执行集成测试,您需要显式执行以下命令:

> gradle runIntegrationTest

这将调用 runIntegrationTest 任务并执行集成测试用例。如果您想在每次构建代码时执行这些测试用例,您可以使用 dependsOn 或任何其他依赖属性将此任务与其他任务链接。

Code coverage


有很多覆盖工具可用于源代码分析,例如 EMMA、Corbatura、JaCoCo 等。在本节中,我们将介绍 Gradle 与 JaCoCo 的集成以查找源代码分析。

在开始之前,我们需要了解什么是代码覆盖率以及为什么它在测试驱动开发中很重要。

代码覆盖率是一个指标,我们可以用它来检查有多少源代码被测试过。更高的代码覆盖率意味着更大比例的代码已经过测试。代码覆盖率通常在单元测试周期中完成。在代码覆盖期间,开发人员必须确保源代码中的不同逻辑路径已经过测试和验证,以实现更好的代码覆盖。

在这里,重要的是要了解代码覆盖率与代码质量没有直接关系。高代码覆盖率并不能保证编写出高质量的代码。开发人员必须使用静态代码分析工具,例如 PMD (https://pmd.github.io/) 来查找代码的质量。要记住的另一点是,即使有 100% 的代码覆盖率,也不能保证编写了完整的无错误代码。因此,许多开发人员认为这不是代码质量或单元测试的正确指标。但是,70-80% 的代码覆盖率被认为是健康代码覆盖率的好数字。

在 Gradle 中,代码覆盖工具 JaCoCo 可以像任何其他插件一样应用于项目:

apply plugin: 'jacoco'

我们的 build.gradle 文件有以下内容。我们创建了一些 TestNG 测试用例来测试源代码的 功能。我们还配置了一个依赖于 jacocoTestReport 任务的测试任务。这是为了确保在运行和创建测试覆盖率报告之前执行测试用例:

apply plugin: 'java'
apply plugin: 'jacoco'

repositories {
  mavenCentral()
}

dependencies {
  testCompile 'org.testng:testng:6.8.8'
}

test{
    systemProperty "url",System.properties['url']
    useTestNG()
}

jacocoTestReport.dependsOn test

默认情况下,将在 <build dir>/reports/jacoco/test/html 目录中创建报告,并生成 HTML 报告文件。例如,我们使用 getter 和 setter 方法创建了一个简单的 POJO User.java 文件。此外,我们还创建了一些单元测试用例来验证功能。两个示例测试用例如下:

  @Test
  public void userEmailTest() {
    User user1 = new User("User2", "User2 user2", "[email protected]");
    Assert.assertEquals(user1.getEmail(), "[email protected]");
  }
  
  @Test
  public void userIdTest() {
    User user1 = new User();
    user1.setUserId("User3");
    user1.setName("User3 user3");
    user1.setEmail("[email protected]");
    Assert.assertEquals(user1.getName(), "User3 user3");
    Assert.assertEquals(user1.getUserId(), "User3");
  }

接下来,我们可以执行jacocoTestReport任务生成代码覆盖率报告:

> gradle clean jacocoTestReport
:clean
:compileJava
:processResources UP-TO-DATE
:classes
:compileTestJava
:processTestResources UP-TO-DATE
:testClasses
:test
:jacocoTestReport

BUILD SUCCESSFUL

Total time: 7.433 secs

在覆盖率报告中,您可以观察到 Java 类的所有方法都经过了单元测试。您可以通过报告中显示源代码行覆盖率的链接进一步深入研究。源代码以绿色和红色标记,以显示已覆盖的内容和未测试的内容。下图(图 7.2)显示了 User.java 类的代码覆盖率统计:

读书笔记《gradle-essentials》使用Gradle进行测试和报告

图 7.2

默认情况下,会在 build/reports/jacoco/test/html 目录中生成一个 HTML 报告文件。另外,jacoco插件的默认版本可以通过修改jacoco 扩展如下:

jacoco {
    toolVersion = "<Required-Version>"
    reportsDir = file("Path_to_Jacoco_ReportDir")
}

同样,可以通过配置 jacocoTestReport 任务来自定义报告,如下所示:

jacocoTestReport {
    reports {
        xml.enabled false
        html.destination "<Path_to_dircectory>"
    }
}

Code analysis reports


Sonar 是最流行的质量管理工具之一,它可以根据代码行数、文档、测试覆盖率、问题和复杂性。作为开发人员,我们主要对以下领域感兴趣:

  • 重复的代码行

  • 源代码中缺少注释,尤其是在公共 API 中

  • 不遵循编码标准和最佳实践

  • 查找代码复杂度

  • 单元测试产生的代码覆盖率

在本节中,我们将讨论 Gradle 与 Sonar 的集成。唯一的先决条件是,Sonar 服务器应该已安装并正在运行。

运行 Sonar 的先决条件是在盒子上安装 Java。满足先决条件后,您只需三个简单的步骤即可安装 Sonar,如下所示:

  1. 下载 发行版http://www.sonarqube.org/downloads/ 并解压。

  2. 打开控制台并启动 Sonar 服务器:

    • 在 Windows 平台上,启动 $SONAR_HOME\bin\windows-x86-32\StartSonar.bat

    • 在其他平台上,启动 $SONAR_HOME/bin/[OS]/sonar.sh

  3. 转到 http://localhost:9000

要运行 sonar-runner 插件,我们只需要应用插件 sonar-runner 并配置它以连接到 Sonar 服务器.

为您的项目创建 构建文件build.gradle,内容如下:

apply plugin: 'groovy'
apply plugin: "sonar-runner"

repositories {
    mavenCentral()
}

version = '1.0'
repositories {
    mavenCentral()
}

sonarRunner {
  sonarProperties {
    property "sonar.host.url", "http://<IP_ADDRESS>:<PORT>"
    property "sonar.jdbc.url",
    "jdbc:h2:tcp://<IP_ADDRESS>:<PORT>/sonar"
    property "sonar.jdbc.driverClassName", "org.h2.Driver"
    property "sonar.jdbc.username", "sonar"
    property "sonar.jdbc.password", "sonar"
  }
}

上面的配置是不言自明的。您需要添加 Sonar URL、DB URL 和 JDBC 驱动程序详细信息等配置,我们的构建文件已准备就绪。

下一步是运行 sonarRunner 任务进行代码分析。成功执行此任务后,您会发现 Sonar 服务器上托管的报告:

>gradle clean sonarRunner
:clean
:compileJava
:processResources UP-TO-DATE
:classes
:compileTestJava
:processTestResources UP-TO-DATE
:testClasses
:test
:sonarRunner
SonarQube Runner 2.3
Java 1.7.0_51 Oracle Corporation (64-bit)
Windows 7 6.1 amd64
INFO: Runner configuration file: NONE
INFO: Project configuration file: <Project_Home>\UserService\build\tmp\sonarRunner\sonar-project.properties
INFO: Default locale: "en_IN", source code encoding: "windows-1252" (analysis is platform dependent)
INFO: Work directory: <Project_Home>\UserService\build\sonar
INFO: SonarQube Server 3.7.4
...
...

现在,您可以打开http://localhost:9000/来浏览项目。此页面是默认仪表板页面,显示所有项目的详细信息。您可以找到您的项目并浏览详细信息。详细信息将显示如下:

读书笔记《gradle-essentials》使用Gradle进行测试和报告

图 7.3

只需点击项目主页中提供的链接,您就可以再次进一步验证每个指标的详细信息。例如,下图显示了 Sonar 中与源代码相关的指标。它提供了诸如代码复杂性、代码行数、方法、文档等详细信息:

读书笔记《gradle-essentials》使用Gradle进行测试和报告

图 7.4

Note

您可以在 声纳的更多信息" target="_blank">http://docs.sonarqube.org/display/SONAR/Documentation/。

Summary


在本章中,我们讨论了 Gradle 的测试和报告方面。我们开始与 TestNG 讨论,还讨论了如何配置 Gradle 以支持与单元测试用例分开的集成测试用例。然后,我们讨论了 JaCoCo 的代码覆盖率,最后,我们讨论了 Sonar 与 Gradle 的集成。

在下一章中,我们将讨论如何在构建脚本和插件中组织构建逻辑。我们将探索如何模块化插件代码,以便它可以在多项目 Gradle 构建中共享。我们还将探索如何在 Gradle 中创建自定义插件。