好,这个是 Java 后端必会核心技能,而且面试 + 实战都会考。
我按「是什么 → 怎么用 → 高级用法 → 常见坑 → 面试点」一步一步给你讲清楚 👌
Java 单元测试 @Test 详解(JUnit 版)
目标:
让你不仅会写 @Test,还知道为什么这么写
一、什么是 Java 单元测试?
单元测试(Unit Test) =
👉 对 最小可测试单元(方法) 进行验证
👉 不依赖数据库、不依赖网络、不依赖外部系统
特点:
- 快(毫秒级)
- 自动化
- 可重复
- 用来 防回归
二、@Test 是什么?
@Test 是 JUnit 提供的注解,标记一个方法是“测试方法”。
JUnit 主要版本:
- JUnit 4(老项目多)
- JUnit 5(Jupiter)(新项目推荐)
三、JUnit 5 最基本用法(推荐)
1️⃣ 引入依赖(Maven)
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
2️⃣ 编写一个最简单的测试
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class MathServiceTest {
@Test
void testAdd() {
int result = 2 + 3;
assertEquals(5, result);
}
}
📌 规则:
@Test方法 不能有参数- 一般
public可省(JUnit 5 支持) - 不返回值(
void)
四、断言(Assertions)才是灵魂
常用断言一览
assertEquals(expected, actual);
assertNotEquals(a, b);
assertTrue(condition);
assertFalse(condition);
assertNull(obj);
assertNotNull(obj);
assertSame(a, b); // 引用相同
assertNotSame(a, b);
示例
@Test
void testUserName() {
User user = new User("Tom");
assertEquals("Tom", user.getName());
}
五、异常测试(必考)
✅ 正确写法(JUnit 5)
@Test
void testDivideByZero() {
assertThrows(ArithmeticException.class, () -> {
int x = 1 / 0;
});
}
❌ 老项目常见(JUnit 4):
@Test(expected = ArithmeticException.class)
六、生命周期注解(非常重要)
JUnit 5 生命周期
@BeforeAll // 类加载后,只执行一次(static)
@AfterAll // 类结束前,只执行一次(static)
@BeforeEach // 每个 @Test 前
@AfterEach // 每个 @Test 后
示例
@BeforeEach
void init() {
System.out.println("初始化");
}
@AfterEach
void cleanup() {
System.out.println("清理");
}
七、测试命名规范(面试加分)
推荐格式
methodName_condition_expectedResult
例:
add_twoNumbers_returnSum()
login_wrongPassword_throwException()
八、@Test + Spring Boot(真实项目)
使用 @SpringBootTest
@SpringBootTest
class UserServiceTest {
@Autowired
private UserService userService;
@Test
void testFindUser() {
User user = userService.findById(1L);
assertNotNull(user);
}
}
⚠️ 注意:
@SpringBootTest启动整个容器- 比普通单元测试 慢很多
- 不算“纯单元测试”,更像 集成测试
九、常见扩展注解(高频)
1️⃣ 禁用测试
@Disabled("暂时不用")
@Test
void testXXX() {}
2️⃣ 重复测试
@RepeatedTest(5)
void testRepeat() {}
3️⃣ 超时测试
@Test
void testTimeout() {
assertTimeout(Duration.ofSeconds(1), () -> {
Thread.sleep(500);
});
}
十、参数化测试(进阶 & 面试亮点)
@ParameterizedTest
@ValueSource(ints = {1, 2, 3})
void testIsPositive(int number) {
assertTrue(number > 0);
}
十一、JUnit 4 vs JUnit 5 对比(必会)
| 对比项 | JUnit 4 | JUnit 5 |
|---|---|---|
| @Test | org.junit.Test | org.junit.jupiter.api.Test |
| 生命周期 | @Before / @After | @BeforeEach / @AfterEach |
| 异常 | expected | assertThrows |
| 扩展 | Runner | Extension |
| 推荐度 | ❌ | ✅ |
十二、单元测试的“红线”(血泪经验)
❌ 测试里写 System.out.println
❌ 测试依赖真实数据库
❌ 测试顺序有依赖
❌ 一个测试测 10 个逻辑
❌ 没有断言
✅ 一个测试只验证 一个行为
✅ 失败信息清晰
✅ 可重复执行
十三、面试高频问题(直接背)
Q:@Test 是做什么的?
标记方法为测试方法,由 JUnit 运行并校验结果。
Q:单元测试和集成测试区别?
单元测试不依赖外部系统,集成测试依赖 Spring、DB、RPC。
Q:为什么要写单元测试?
防回归、提高代码质量、提升重构信心。
十四、推荐组合(真实项目)
- JUnit 5
- Mockito(Mock 依赖)
- AssertJ(断言更优雅)
- Jacoco(覆盖率)
如果你愿意,我可以继续帮你:
- 🔥 Mockito + @Test 实战(Mock Service / Mapper)
- 🔥 Spring Boot 单元测试最佳实践
- 🔥 一套面试级单元测试模板
你现在是 在写项目,还是 准备 Java 后端面试?