大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
测试与 JUnit
可靠重构依赖 自动化测试。java-fundamentals 可能已跑过 mvn test——本课用 JUnit 5 与 Mockito 写可维护的单元测试,衔接 testing-essentials。
学习目标
- 编写 JUnit 5 测试方法与断言
- 用
@BeforeEach、参数化测试组织用例 - 用 Mockito mock 外部依赖
JUnit 5 基础
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
class TrackCatalogTest {
private TrackCatalog catalog;
@BeforeEach
void setUp() {
catalog = new TrackCatalog("java-core-essentials");
}
@Test
void addLesson_rejectsDuplicateSlug() {
catalog.add(new Lesson("jvc-types", "类型与变量"));
assertThrows(IllegalArgumentException.class,
() -> catalog.add(new Lesson("jvc-types", "重复")));
}
@Test
void lessons_sortedByOrder() {
catalog.add(new Lesson("b", "B", 2));
catalog.add(new Lesson("a", "A", 1));
assertEquals(List.of("a", "b"),
catalog.lessons().stream().map(Lesson::slug).toList());
}
}
| 注解 | 作用 |
|---|---|
@Test | 测试方法 |
@BeforeEach / @AfterEach | 每个测试前后 |
@DisplayName | 可读测试名 |
@ParameterizedTest | 多组输入 |
参数化与断言
@ParameterizedTest
@ValueSource(strings = { "jvc-types", "jvc-control", "jvc-oop" })
void slug_startsWithJvc(String slug) {
assertTrue(slug.startsWith("jvc-"));
}
优先 具体断言(assertEquals)而非只 assertTrue(a.equals(b));失败时信息更清晰。
Mockito
@ExtendWith(MockitoExtension.class)
class LessonServiceTest {
@Mock LessonRepository repository;
@InjectMocks LessonService service;
@Test
void findBySlug_delegatesToRepository() {
when(repository.findBySlug("jvc-test")).thenReturn(Optional.of(mockLesson()));
assertTrue(service.findBySlug("jvc-test").isPresent());
verify(repository).findBySlug("jvc-test");
}
}
- mock 隔离被测类与数据库/HTTP
- when/thenReturn 桩行为;verify 检查交互
- 不要 mock 值对象;领域逻辑用 真实对象(见 jvc-practice)
练习
- 为
TrackCatalog.add写至少 3 个测试(成功、重复、null slug) - 用
@TempDir测文件读写(配合 jvc-io) - mock 一个
Clock或接口,测时间相关逻辑
小结
JUnit 验行为,Mockito 替依赖;测试名说清场景。 下一课综合练习:Stream + 单元测试小模块。