Java 单元测试详解:从入门到实战,彻底掌握 JUnit 5 + Mockito + Spring Boot 测试技巧
作为一名 Java 开发工程师,你一定知道在软件开发中,代码的可维护性、可扩展性和质量保障是项目成功的关键。而单元测试(Unit Testing) 正是确保代码质量、提升开发效率、减少 Bug 的核心手段之一。
本文将带你全面掌握:
- 什么是单元测试?
- 为什么需要单元测试?
- Java 常用的单元测试框架(JUnit 5、TestNG、Mockito)
- 如何为 Java 类、Spring Boot 项目编写单元测试
- 使用断言、Mock、Spy、参数化测试等高级技巧
- 最佳实践与常见误区
并通过丰富的代码示例和真实项目场景讲解,帮助你写出更规范、更高效、更易维护的 Java 单元测试代码。
🧱 一、什么是单元测试?
✅ 单元测试(Unit Testing)定义:
单元测试是针对**最小可测试单元(通常是方法)**进行正确性验证的测试,通常由开发者编写,用于验证某个类或方法在特定输入下是否返回预期结果。
✅ 单元测试的特点:
🔍 二、Java 常见的单元测试框架对比
📌 推荐组合: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
@BeforeEach
、@AfterEach
@SpringBootTest
进行集成测试🚫 八、常见误区与注意事项
shouldXXX_whenXXX
命名assertEquals
、assertTrue
等断言@ExtendWith
或 @SpringBootTest
assertThrows
测试异常📊 九、总结:Java 单元测试核心知识点一览表
assertEquals
、assertTrue
、assertThrows
@ParameterizedTest
@WebMvcTest
、@DataJpaTest
、@SpringBootTest
📎 十、附录:Java 单元测试常用技巧速查表
@ExtendWith(MockitoExtension.class)
@Mock
、@InjectMocks
assertEquals(expected, actual)
assertThrows(Exception.class, () -> method())
@ParameterizedTest
+ @CsvSource
verify(mock, times(1)).method()
@WebMvcTest
+ MockMvc
@DataJpaTest
@SpringBootTest
欢迎点赞、收藏、转发,也欢迎留言交流你在实际项目中遇到的单元测试相关问题。我们下期再见 👋
📌 关注我,获取更多Java核心技术深度解析!