← Backend / Java

20_Java 构建工具与测试

Maven/Gradle 项目结构、JUnit 5、集成测试、依赖管理和可重复构建。

Java 构建工具与测试

学习目标:理解依赖、编译、单元测试与集成测试如何进入可重复的构建流程。

1. Maven 与 Gradle 的职责

构建工具负责下载依赖、编译、运行测试、打包和发布。Maven 以 pom.xml 与生命周期为核心;Gradle 以构建脚本和任务为核心。任选一个先学透,不必同时把两套 DSL 背下来。

src/main/java/       # 生产代码
src/main/resources/  # 配置与资源
src/test/java/       # 测试代码

常见命令为 mvn test、mvn package,或 ./gradlew test、./gradlew build。优先使用项目提供的 Maven/Gradle Wrapper,并在项目中固定 Java 与插件版本;不要依赖某台电脑上的“刚好能编译”。

2. JUnit 5 单元测试

以下测试假设项目已引入 JUnit Jupiter 依赖。

import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;

class PriceCalculatorTest {
    @Test
    void appliesDiscount() {
        int price = 100;
        int discount = 15;
        assertEquals(85, price - discount);
    }
}

这个例子只展示测试结构;真实测试应调用生产方法,而非在测试中重写同一算法。优先覆盖边界值、非法输入和业务失败分支。

3. 集成测试

数据库集成测试验证 SQL、约束、迁移与事务行为。使用独立测试库或临时容器,不接生产实例。Spring Boot 的 Web 层测试可验证状态码与序列化;完整上下文测试再验证各层接线。测试越接近真实系统越慢,因此应按风险组合,而非把所有测试都变成端到端测试。

易错点:只在自己机器上通过不代表构建可重复。CI 应从干净环境安装依赖、运行测试并构建产物。

4. 依赖与发布

锁定直接依赖和关键插件版本,定期检查已知漏洞;分清编译依赖、运行依赖和仅测试依赖。构建产物、配置和密钥分离;部署时使用同一构建产物经过测试、预发和生产,而非各环境各自重编。

自测

  1. 单元测试应连接真实生产数据库吗?不应。
  2. 为什么应使用构建 Wrapper?它固定工具版本,减少环境差异。

5. 写真正验证生产代码的测试

一个有效单元测试应调用被测方法,并对边界和失败路径作断言。测试名描述行为,避免在测试里重新写一份业务算法,否则两份同样错误的代码可能一起通过。

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;

class TitleService {
    static String normalize(String title) {
        if (title == null || title.isBlank())
            throw new IllegalArgumentException("title required");
        return title.trim();
    }
}

class TitleServiceTest {
    @Test void trimsTitle() {
        assertEquals("hello", TitleService.normalize("  hello  "));
    }
    @Test void rejectsBlankTitle() {
        assertThrows(IllegalArgumentException.class,
                () -> TitleService.normalize("  "));
    }
}

示例把生产代码和测试展示在一处便于阅读;真实项目分别放在 src/main/java 与 src/test/java。assertThrows 验证异常类型,必要时还要验证稳定的错误代码和副作用是否回滚。

6. 构建流水线的最小顺序

  1. 使用 Wrapper 和固定 JDK 版本,从干净环境恢复依赖。
  2. 运行编译、静态检查与单元测试;失败即停止。
  3. 启动隔离数据库,执行迁移和集成测试。
  4. 只打包通过测试的源码,记录版本和构建来源;同一产物进入后续环境。

依赖管理要区分直接与传递依赖。版本升级时不仅看编译是否通过,还要检查行为变化和已知漏洞。