> 技术文档 > Java 单元测试详解:从入门到实战,彻底掌握 JUnit 5 + Mockito + Spring Boot 测试技巧

Java 单元测试详解:从入门到实战,彻底掌握 JUnit 5 + Mockito + Spring Boot 测试技巧

作为一名 Java 开发工程师,你一定知道在软件开发中,代码的可维护性、可扩展性和质量保障是项目成功的关键。而单元测试(Unit Testing) 正是确保代码质量、提升开发效率、减少 Bug 的核心手段之一。

本文将带你全面掌握:

  • 什么是单元测试
  • 为什么需要单元测试?
  • Java 常用的单元测试框架(JUnit 5、TestNG、Mockito)
  • 如何为 Java 类、Spring Boot 项目编写单元测试
  • 使用断言、Mock、Spy、参数化测试等高级技巧
  • 最佳实践与常见误区

并通过丰富的代码示例和真实项目场景讲解,帮助你写出更规范、更高效、更易维护的 Java 单元测试代码。


🧱 一、什么是单元测试?

✅ 单元测试(Unit Testing)定义:

单元测试是针对**最小可测试单元(通常是方法)**进行正确性验证的测试,通常由开发者编写,用于验证某个类或方法在特定输入下是否返回预期结果。

✅ 单元测试的特点:

特点 描述 自动化 无需人工执行,可自动运行 快速执行 单个测试用例执行时间极短 独立运行 不依赖外部系统(如数据库、网络) 可重复执行 每次运行结果一致 可集成CI/CD 与 Jenkins、GitLab CI、GitHub Actions 等集成 提高代码质量 减少 Bug、提升重构信心

🔍 二、Java 常见的单元测试框架对比

框架 特点 JUnit 5 最主流的 Java 单元测试框架,支持 Java 8+,模块化设计 TestNG 支持数据驱动、依赖测试、并行测试,适合复杂测试场景 Mockito 用于模拟对象(Mock)、验证行为(Verify) PowerMock 可 Mock 静态方法、构造函数等(已逐渐被替代) AssertJ 提供更流畅的断言语法,增强可读性 Spring Boot Test 集成 JUnit + Mockito,支持 Spring 上下文加载

📌 推荐组合:JUnit 5 + Mockito + Spring Boot Test,适用于大多数 Java Web 项目。


🧠 三、JUnit 5 核心概念与注解

✅ 常用注解:

注解 说明 @Test 表示一个测试方法 @BeforeEach 每个测试方法执行前执行 @AfterEach 每个测试方法执行后执行 @BeforeAll 所有测试方法执行前执行一次(静态方法) @AfterAll 所有测试方法执行后执行一次(静态方法) @DisplayName 设置测试类或方法的显示名称 @ParameterizedTest 参数化测试 @RepeatedTest 重复执行测试方法

🧪 四、JUnit 5 实战示例

示例1:简单单元测试

import org.junit.jupiter.api.*;import static org.junit.jupiter.api.Assertions.*;public class CalculatorTest { private Calculator calculator; @BeforeEach void setUp() { calculator = new Calculator(); } @Test @DisplayName(\"两个整数相加应返回正确结果\") void add_twoNumbers_returnsSum() { int result = calculator.add(2, 3); assertEquals(5, result, \"2+3 应该等于5\"); } @Test void divide_byZero_throwsException() { Exception exception = assertThrows(ArithmeticException.class, () -> { calculator.divide(10, 0); }); assertEquals(\"/ by zero\", exception.getMessage()); }}

示例2:参数化测试(@ParameterizedTest

import org.junit.jupiter.params.ParameterizedTest;import org.junit.jupiter.params.provider.CsvSource;import static org.junit.jupiter.api.Assertions.*;class CalculatorTest { @ParameterizedTest @CsvSource({ \"2, 3, 5\", \"5, 5, 10\", \"0, 0, 0\" }) void add_withDifferentInputs_returnsCorrectResult(int a, int b, int expected) { Calculator calculator = new Calculator(); assertEquals(expected, calculator.add(a, b)); }}

🧩 五、使用 Mockito 模拟对象(Mock)

✅ 什么是 Mock?

Mock 是指模拟对象的行为,在测试中避免依赖外部系统(如数据库、网络、第三方服务),从而实现快速、独立、可重复的测试

示例:Mock 一个外部服务

import static org.mockito.Mockito.*;import static org.junit.jupiter.api.Assertions.*;class OrderServiceTest { @Test void placeOrder_callsPaymentServiceOnce() { PaymentService mockPayment = mock(PaymentService.class); OrderService orderService = new OrderService(mockPayment); orderService.placeOrder(100.0); verify(mockPayment, times(1)).charge(100.0); }}

🧪 六、Spring Boot 单元测试实战

示例:Spring Boot Controller 单元测试(MockMvc)

import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;import org.springframework.test.web.servlet.MockMvc;import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;@WebMvcTest(UserController.class)class UserControllerTest { @Autowired private MockMvc mockMvc; @Test void getUserById_returnsUserJson() throws Exception { mockMvc.perform(get(\"/users/1\"))  .andExpect(status().isOk())  .andExpect(jsonPath(\"$.name\").value(\"Tom\")); }}

示例:Service 层单元测试(注入 Mock)

import org.junit.jupiter.api.Test;import org.junit.jupiter.api.extension.ExtendWith;import org.mockito.InjectMocks;import org.mockito.Mock;import org.mockito.junit.jupiter.MockitoExtension;import static org.mockito.Mockito.*;import static org.junit.jupiter.api.Assertions.*;@ExtendWith(MockitoExtension.class)class UserServiceTest { @Mock private UserRepository userRepository; @InjectMocks private UserService userService; @Test void getUserById_returnsUserFromRepository() { when(userRepository.findById(1L)).thenReturn(new User(\"Tom\")); User user = userService.getUserById(1L); assertNotNull(user); assertEquals(\"Tom\", user.getName()); verify(userRepository, times(1)).findById(1L); }}

🧱 七、单元测试最佳实践

实践 描述 一个测试方法只测一个功能 保证测试单一职责 测试命名清晰、有语义 如 shouldReturnTrue_whenInputIsEven 使用断言库(JUnit、AssertJ) 提高可读性 使用 Mock 避免外部依赖 提高测试速度与稳定性 覆盖核心逻辑和边界条件 包括 null、异常、边界值等 使用参数化测试减少重复代码 提高测试覆盖率 在 CI/CD 中集成测试 确保每次提交都运行测试 使用覆盖率工具(Jacoco) 查看测试覆盖率 测试前准备、测试后清理 使用 @BeforeEach@AfterEach 使用 @SpringBootTest 进行集成测试 验证完整流程

🚫 八、常见误区与注意事项

误区 正确做法 单元测试依赖数据库 应使用 Mock 或内存数据库 测试方法不命名规范 应使用 shouldXXX_whenXXX 命名 不断言直接 return 必须使用 assertEqualsassertTrue 等断言 测试类没有注解 应使用 @ExtendWith 或 @SpringBootTest 忽略异常测试 应使用 assertThrows 测试异常 一个测试方法测多个功能 应拆分为多个测试方法 不使用参数化测试 导致重复代码 不使用断言库 代码可读性差 不在 CI 中运行测试 容易漏测 不使用覆盖率工具 无法评估测试质量

📊 九、总结:Java 单元测试核心知识点一览表

内容 说明 单元测试定义 针对最小单元(方法)进行测试 主流框架 JUnit 5、Mockito、Spring Boot Test 断言机制 assertEqualsassertTrueassertThrows Mock 技术 使用 Mockito 模拟对象 参数化测试 @ParameterizedTest Spring Boot 测试 @WebMvcTest@DataJpaTest@SpringBootTest 最佳实践 命名规范、单一职责、Mock 依赖、CI 集成 注意事项 不依赖外部系统、使用断言、测试覆盖率

📎 十、附录:Java 单元测试常用技巧速查表

技巧 示例 初始化测试类 @ExtendWith(MockitoExtension.class) 模拟对象 @Mock@InjectMocks 断言相等 assertEquals(expected, actual) 断言异常 assertThrows(Exception.class, () -> method()) 参数化测试 @ParameterizedTest + @CsvSource 验证调用次数 verify(mock, times(1)).method() Spring Boot 控制器测试 @WebMvcTest + MockMvc 数据层测试 @DataJpaTest 集成测试 @SpringBootTest 测试覆盖率 使用 Jacoco 或 IntelliJ 内置工具

欢迎点赞、收藏、转发,也欢迎留言交流你在实际项目中遇到的单元测试相关问题。我们下期再见 👋

📌 关注我,获取更多Java核心技术深度解析!

银行利率表