【JUnit实战3_20】第十一章:用 Gradle 运行 JUnit 测试实战

《JUnit in Action》全新第3版封面截图

写在前面

一直都想尝试用 Gradle 构建项目,毕竟 Gradle 的轻量和灵活总是让围城外面的人向往不已,但学习曲线的陡峭也让人望而却步。本书第 11 章刚出版时演示的最新版是 6.0,现在已经到 9.1 并且默认支持 Kotlin 而不再是 Groovy 了。但既然是实战类笔记,就必须在本地跑跑最新版,看看有什么不一样的地方。

第十一章:用 Gradle 运行 JUnit 测试

本章概要

  • Gradle 简介
  • Gradle 项目的设置
  • Gradle 插件的用法
  • 从零创建 Gradle 项目、并用 JUnit 5 进行测试
  • 对比 GradleMaven

本章对 Gradle 介绍十分简要,因为截止 2020 年成书时,全球使用 Gradle 构建的项目也仅占约 25%,近年来的市场份额也没有太大的变化;再加上 Gradle 本身的学习曲线比较陡峭,当时最新的 v6.0 版对 @Display 注解的支持也存在一些问题,相关介绍就更少了。因此,实测本章内容时将采用最新的 v9.0.0 进行验证,并结合 附录 B 对一些基础用法做一些介绍。


11.1 Gradle 简介

Gradlehttps://gradle.org)是继 Apache Ant(2000 年)和 Apache Maven(2004 年)后,由 Google 于 2012 年推出的、基于 GroovyDSL 语言开发的新一代项目构建工具。

Gradle(读作:/ˈɡreɪdəl/),该名称的设计灵感源于 GroovyAnt 的结合[1](#1);其单词本意为等级、阶梯、渐变,这也恰好暗含了 Gradle 的另一个关键特性:增量构建Gradle 的问世主要是为了在 Ant 的灵活性和 Maven 的规范性之间找到一个最佳平衡点,并利用一门真正的编程语言(即 Groovy,目前默认使用 Kotlin)来打破 XML 的固有局限性。

知识拓展:增量构建

所谓的增量构建,是指 Gradle 能通过分析任务的输入和输出,智能地判断哪些任务是最新的、哪些需要重新执行。由于只执行那些"已升级"或"已变更"的部分,从而极大地提升了构建速度。这种 一步步升级/构建 的过程,与 阶梯 的意象非常吻合。

Groovy 是一门典型的 DSL(domain-specific language,特定领域语言) 语言。它兼容 Java 语法,支持面向对象编程,且可以在 Java 虚拟机上运行。

DSL 是一种专门解决特定应用领域问题的计算机语言,常见的还包括 SQL、正则表达式、AWKScalaKotlin 等;其核心理念是为了 在特定领域提升表达效率和清晰度,让代码或配置读起来更像是某个领域的专家在描述问题,而不是程序员编写的通用逻辑。

DSL 概念相对立的另一个概念即 GPLGeneral-Purpose Language,即通用编程语言 ),例如 JavaPythonC++ 等语言。GPL 旨在解决各种领域的复杂计算问题,能够提供完备的编程抽象(变量、循环、函数等),功能强大,但学习曲线也相对陡峭。

而在项目构建领域,践行 DSL 理念的一个重要成果,就是基于 Groovy 语言的 Gradle

11.2 Gradle 的安装与基础配置

本节根据 附录 B 整理,实测时 Gradle 官网 已经正式发布 v9.1.0

对应的 Java 版本也升级到了 JDK17。为了增强实测的时效性,本地选用最新版进行演示。

zip 安装包下载地址:https://gradle.org/releases/

下载成功后,将 Gradle 安装包解压到某个目录下(如 D:\gradle-9.1.0\):

然后按如下步骤配置环境变量:

powershell 复制代码
# 打开系统高级设置窗口
> systemPropertiesAdvanced

新建环境变量 GRADLE_HOME,对应的值为刚才解压的 Gradle 路径 (D:\gradle-9.1.0\):

然后调用该变量,将 %GRADLE_HOME%\bin 添加到 Path 中:

再用同样的方式创建一个系统变量 GRADLE_USER_HOME,对应的值为本地 Maven 仓库的路径,这样就能和 Maven 共享本地依赖;否则 Gradle 在构建项目时会默认在 {USER_HOME}/.gradle/ 文件夹下重新下载所需依赖,浪费磁盘空间:

确认后验证配置:

powershell 复制代码
> gradle -version

------------------------------------------------------------
Gradle 9.1.0
------------------------------------------------------------

Build time:    2025-09-18 13:05:56 UTC
Revision:      e45a8dbf2470c2e2474ccc25be9f49331406a07e

Kotlin:        2.2.0
Groovy:        4.0.28
Ant:           Apache Ant(TM) version 1.10.15 compiled on August 25 2024
Launcher JVM:  21.0.6 (Oracle Corporation 21.0.6+8-LTS-188)
Daemon JVM:    C:\Program Files\Java\jdk-21 (no JDK specified, using current Java home)
OS:            Windows 10 10.0 amd64
> 

说明 Gradle 安装成功。

11.3 用 Gradle 创建一个项目

本节演示如何利用 Gradle 命令创建一个默认项目。

任意新建一个文件夹(例如上一章的 C:\junitbook\),然后命令行导航到该路径,并执行如下命令:

bash 复制代码
> mkdir flightmanagement | Out-Null
> cd flightmanagement
> gradle init
Starting a Gradle Daemon, 1 stopped Daemon could not be reused, use --status for details

Select type of build to generate:
  1: Application
  2: Library
  3: Gradle plugin
  4: Basic (build structure only)
Enter selection (default: Application) [1..4]

Select implementation language:
  1: Java
  2: Kotlin
  3: Groovy
  4: Scala
  5: C++
  6: Swift
Enter selection (default: Java) [1..6]

Enter target Java version (min: 7, default: 21):

Project name (default: flightmanagement):

Select application structure:
  1: Single application project
  2: Application and library project
Enter selection (default: Single application project) [1..2]

Select build script DSL:
  1: Kotlin
  2: Groovy
Enter selection (default: Kotlin) [1..2] 2

Select test framework:
  1: JUnit 4
  2: TestNG
  3: Spock
  4: JUnit Jupiter
Enter selection (default: JUnit Jupiter) [1..4]

Generate build using new APIs and behavior (some features may change in the next minor release)? (default: no) [yes, no]


> Task :init
Learn more about Gradle by exploring our Samples at https://docs.gradle.org/9.1.0/samples/sample_building_java_applications.html

BUILD SUCCESSFUL in 50s
1 actionable task: 1 executed
> 

注意,最新版 Gradle 默认的构建脚本已经切换到 Kotlin 了,因此应该手动选择 Groovy,输入 2 后按 Enter 键继续(第 L34 行)。其余选项均保持默认设置即可。最后得到的项目结构如下:

powershell 复制代码
> tree /f .
Folder PATH listing for volume OS
Volume serial number is 00000085 DA4A:6CB8
C:\JUNITBOOK\FLIGHTMANAGEMENT
│   .gitattributes
│   .gitignore
│   gradle.properties
│   gradlew
│   gradlew.bat
│   settings.gradle
│
├───app
│   │   build.gradle
│   │
│   └───src
│       ├───main
│       │   ├───java
│       │   │   └───org
│       │   │       └───example
│       │   │               App.java
│       │   │
│       │   └───resources
│       └───test
│           ├───java
│           │   └───org
│           │       └───example
│           │               AppTest.java
│           │
│           └───resources
└───gradle
    │   libs.versions.toml
    │
    └───wrapper
            gradle-wrapper.jar
            gradle-wrapper.properties
> 

接着删除默认生成的 org.example.App.java 以及对应的测试类 org.example.AppTest.java,改为书中指定的 Passenger 类和测试类 PassengerTest

powershell 复制代码
# 删除默认的类和测试类
> rm -recurse -Force app\src\main\java\org, app\src\test\java\org

导入 IDEA 后添加新的 Passenger 类和测试类 PassengerTest

java 复制代码
// 乘客实体类
package com.testeddatasystem.flights;

public class Passenger {
    private String identifier;
    private String name;

    public Passenger(String identifier, String name) {
        this.identifier = identifier;
        this.name = name;
    }

    public String getIdentifier() {
        return identifier;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "Passenger " + getName() +", with identifier: " + getIdentifier();
    }

    public static void main(String[] args) {
        final Passenger passenger = new Passenger("123-456-789", "John Smith");
        System.out.println(passenger);
    }
}

然后在 Passenger 类上按 Ctrl + Shift + T 自动创建测试类:

添加测试逻辑如下:

java 复制代码
package com.testeddatasystem.flights;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class PassengerTest {

    @Test
    @DisplayName("toString() method should match the desired content")
    void testToString() {
        final Passenger passenger = new Passenger("123-456-789", "John Smith");
        assertEquals("Passenger John Smith, with identifier: 123-456-789",
                passenger.toString());
    }
}

Shift + F10 快速运行该测试用例,得到如下结果:

Maven 不同的是,Gradle 会在 app/build/ 目录下直接生成 HTML 格式的测试报表:

用浏览器打开该文件,还可以看到原书中提到的 @DisplayName 注解显示异常的问题早已解决:

最后,再按书中演示的步骤过一遍 Gradle 的相关构建命令。

先修改配置文件 app/build.gradle 中的主类引用:

groovy 复制代码
application {
    // Define the main class for the application.
    mainClass = 'com.testeddatasystem.flights.Passenger'
}

然后运行命令 gradle run

接着执行命令 gradle build

最后是 gradle test

Gradle 9.1.0 最新的默认配置脚本已经和书中的 6.0 大相径庭了(9.1 版已无法顺利编译 6.0 的配置脚本了),新 build.gradle 的完整内容如下:

groovy 复制代码
/*
 * This file was generated by the Gradle 'init' task.
 *
 * This generated file contains a sample Java application project to get you started.
 * For more details on building Java & JVM projects, please refer to https://docs.gradle.org/9.1.0/userguide/building_java_projects.html in the Gradle documentation.
 */

plugins {
    // Apply the application plugin to add support for building a CLI application in Java.
    id 'application'
}

repositories {
    // Use Maven Central for resolving dependencies.
    mavenCentral()
}

dependencies {
    // Use JUnit Jupiter for testing.
    testImplementation libs.junit.jupiter

    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

    // This dependency is used by the application.
    implementation libs.guava
}

// Apply a specific Java toolchain to ease working on different environments.
java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}

application {
    // Define the main class for the application.
    mainClass = 'com.testeddatasystem.flights.Passenger'
}

tasks.named('test') {
    // Use JUnit Platform for unit tests.
    useJUnitPlatform()
}

关于上述配置的具体含义,详见 Gradle 官方文档:https://docs.gradle.org/current/userguide/userguide.html

想深入了解 Gradle 各种用法的朋友,还可以参考作者推荐的一本参考书:《Gradle in Action 》(Benjamin Muschko, Manning, 2014.02)。

11.4 拓展:Gradle 任务的创建

附录 B 中还介绍了几个关于 Gradle 任务的小例子,这里也在本地实测了一遍。

Gradle 是通过 构建文件build file)来管理项目。每个 Gradle 构建文件都能描述 一个或多个 项目,而项目则是由不同的 任务 构成。所谓的 Gradle 任务,是指通过运行构建文件执行的某项工作内容,如编译某些类、创建 JAR 包、生成 Javadoc 文档、或将归档文件发布到 repository 仓库等等。构建或测试项目所需的操作都可以通过 Gradle 任务来定义和执行。

Gradle 还有个 闭包 的概念。闭包代表了某个独立的代码块,既可以接收参数,也可以有返回值。

Gradle 构建文件的默认名称为 build.gradle,因此可以利用该文件在桌面新建一个示例文件夹 gradleTest,然后执行下列命令,体验一下 Gradle 任务的相关操作:

powershell 复制代码
# 初始化示例文件夹
> (pwd).Path
C:\Users\ad\Desktop
> mkdir gradleTest | Out-Null
> cd gradleTest

# 示例1:创建一个 gradle 配置脚本
> vim build.gradle
> cat build.gradle
task junit {
  print "JUnit in Action"
}
# 执行 gradle 任务 junit(静默模式)
> gradle -q junit
JUnit in Action 0% CONFIGURING [79ms]

# 示例2:运行带依赖关系的 gradle 任务
> vim build.gradle
> cat build.gradle
task junit {
  print "JUnit in Action"
}

task third (dependsOn: 'junit') {
  println ", third edition"
}
# 执行任务 third
> gradle -q third
JUnit in Action, third edition

# 示例3:运行带依赖关系、且任务中包含多个阶段的复杂任务
> vim build.gradle
> cat build.gradle
task junit {
  print "JUnit "

  doFirst {
    print "Action"
  }

  doLast {
    print ", "
  }
}

task third (dependsOn: 'junit') {
  print "in "

  doFirst {
    print "third "
  }

  doLast {
    println "edition"
  }
}
# 执行复杂任务 third
> gradle -q third
JUnit in Action, third edition
> 

其中的 dependsOndoFirstdoLast 都是 Gradle API 中的固定写法,不可随意变更。具体用法详见 Gradle 官方文档

11.5 对比 GradleMaven

11.5.1 核心理念对比

Maven Gradle
主要特点 声明式、标准化 声明式 + 命令式、高度灵活
构建语言 XML (pom.xml) Groovy / KotlinDSL 语言(build.gradlebuild.gradle.kts
核心原则 约定优于配置 约定优于配置,但可轻松覆盖约定配置

11.5.2 Ant、Maven、Gradle 横向对比

由于书中并未展开讨论 MavenGradle 的区别和联系,这里借用 Code0cean 大佬在博文《Gradle快速入门学习》中归纳的异同点:

对比维度 Ant Maven Gradle
构建性能 最高 最低 居中
仓库维护 开发者自行维护 Maven 仓库 支持多种远程仓库
依赖管理 Ivy 管理 GAV 坐标管理 GNV 坐标管理
插件支持 实现方便 实现较难 实现方便
遵循特定目录结构 ✔️ ✔️
配置文件 XML 文件(最繁琐) XML 文件 代码脚本,便于写业务逻辑
侧重点 小型项目构建 项目包管理 大型项目构建
目前地位 使用较少 主流 未来趋势

11.5.3 构建工具选型建议

场景 推荐工具 理由
传统 Java EE / Spring 项目 Maven 项目结构标准,依赖管理简单,团队学习成本低,稳定性高。
Android 开发 Gradle 官方唯一支持,无其他选择。
大型、复杂、多项目构建 Gradle 卓越的性能和灵活性,能有效管理复杂的项目依赖和构建逻辑。
需要高度自定义构建流程 Gradle 用代码编写构建逻辑的能力是决定性优势。
初创团队或快速原型 Maven 简单的 pom.xml 可以快速搭建项目,避免在构建工具上花费过多时间。
追求极致构建速度 Gradle 增量构建和构建缓存带来的性能提升是碾压性的。

  1. Gradle 创始人 Hans Dockter 曾在多次访谈和演讲中提到,该名字意在传达它是一款基于 Groovy 的、建立在 Ant 最佳理念之上的构建工具,并将其提升到一个新的水平。 ↩︎
相关推荐
张永清-老清5 小时前
每周读书与学习->JMeter主要元件详细介绍(三)逻辑控制器
测试工具·jmeter·压力测试·性能调优·jmeter性能测试·性能分析·每周读书与学习
程序员三藏6 小时前
如何使用Selenium做自动化测试?
自动化测试·软件测试·python·selenium·测试工具·职场和发展·测试用例
蜕变的土豆9 小时前
三、cmake语法-提高篇
c++·软件构建
安冬的码畜日常9 小时前
【JUnit实战3_19】第十章:用 Maven 3 运行 JUnit 测试(下)
java·测试工具·junit·单元测试·maven·junit5
董广明9 小时前
单元测试(JUnit、Mockito、PowerMock )
java·经验分享·junit·单元测试
安冬的码畜日常9 小时前
【JUnit实战3_18】第十章:用 Maven 3 运行 JUnit 测试(上)
测试工具·junit·maven·artifact·junit5
ClassOps9 小时前
Gradle Groovy 和 Kotlin kts 语法对比
android·kotlin·gradle·groovy
交换机路由器测试之路11 小时前
发包工具anysend使用手册
网络·测试工具·ipv6·发包工具
jc062014 小时前
7.1-性能与测试工具
测试工具