
本文讲解在 JUnit 5 中如何正确验证数据库唯一性约束(如重复手机号)触发的预期异常,指出误用 assertDoesNotThrow() 的常见错误,并演示使用 assertThrows() 进行断言的规范写法。
本文讲解在 junit 5 中如何正确验证数据库唯一性约束(如重复手机号)触发的预期异常,指出误用 `assertdoesnotthrow()` 的常见错误,并演示使用 `assertthrows()` 进行断言的规范写法。
在编写单元测试验证数据完整性规则时,一个典型场景是:当尝试插入具有重复唯一字段(如手机号、邮箱)的实体时,数据库应拒绝该操作并抛出异常。此时,测试的目标不是“不抛异常”,而是“必须抛出特定类型的异常”——这恰恰是 assertDoesNotThrow() 的语义所违背的。
你当前的测试代码:
@Test
@Order(3)
void createCandidate_DuplicatePhone_Test() {
Candidate candidate = Candidate.builder()
.fullName("Name")
.dateOfBirth(LocalDate.of(2001, 10, 1))
.gender(Gender.MALE)
.graduationYear(LocalDate.of(2023, 10, 10))
.phone("999999999") // ← 假设该号码已存在于数据库中
.email("test@example.com")
.skill("java")
.foreignLanguage("English")
.level(6)
.cv("Good")
.allocationStatus(1)
.remark("Not Bad")
.build();
assertDoesNotThrow(() -> candidateDAO.create(candidate)); // ❌ 错误:期望无异常,但约束违规必然抛异常
}
该写法隐含假设“插入重复手机号应成功”,而实际业务与数据库设计要求:重复手机号必须失败。因此,正确的测试逻辑应为:
✅ 明确断言:调用 candidateDAO.create(candidate) 应抛出 PersistenceException(或更具体的 ConstraintViolationException)。
✅ 推荐写法:使用 assertThrows()
@Test
@Order(3)
void createCandidate_DuplicatePhone_Test() {
// 准备一个已存在的手机号(确保数据库中已有同号记录)
String duplicatePhone = "999999999";
Candidate candidate = Candidate.builder()
.fullName("Duplicate Tester")
.phone(duplicatePhone)
.email("duplicate@example.com")
.build();
// 断言:必须抛出 PersistenceException(或其子类)
PersistenceException exception = assertThrows(PersistenceException.class,
() -> candidateDAO.create(candidate));
// (可选)进一步验证异常原因是否为约束冲突
assertTrue(exception.getCause() instanceof ConstraintViolationException,
"Expected ConstraintViolationException as root cause");
}
? 提示:若
candidateDAO.create()底层使用 Hibernate,实际抛出的通常是jakarta.persistence.PersistenceException,其getCause()为org.hibernate.exception.ConstraintViolationException。建议捕获PersistenceException并检查根本原因,以提高测试健壮性与可读性。
⚠️ 注意事项
-
测试前需准备数据:确保数据库中已存在相同手机号的记录(可通过
@BeforeEach插入,或使用@Sql加载 SQL 脚本); -
事务管理:若测试类启用 Spring Test 事务(
@Transactional),需注意默认回滚可能影响重复数据准备——建议对数据准备逻辑单独控制事务,或使用@Commit/@Rollback(false)显式管理; -
避免模糊断言:不要仅用
assertThrows(Exception.class, ...),应尽量指定最具体的异常类型,提升失败诊断效率; -
清理非必要依赖:该测试聚焦“重复校验”,无需设置全部字段(如
cv、remark等),精简参数可增强可读性与维护性。
通过正确使用 assertThrows(),你不仅能准确验证业务规则的强制执行,还能让测试真正成为系统契约的可靠守护者。










