单元/集成测试可无断言吗?关于删除集成测试的拆分疑问
Great question—let’s break this down clearly, since these are super common pitfalls when writing database tests!
1. 每个测试都必须包含断言吗?
绝对是的。没有断言的测试本质上只是在“执行代码而不验证结果”——就像你按下删除按钮,只确认没弹出错误提示,但根本没检查东西真的被删掉了。
你的第一个delete()测试只验证方法不抛出异常,但这几乎没有价值:如果rfxCriteriRepository.delete()里的逻辑不小心被注释掉了,这个测试依然会通过,但功能完全失效。测试的核心目标是验证预期行为是否发生,断言就是实现这个目标的唯一方式。哪怕是验证“不抛出异常”,也应该用Assertions.assertDoesNotThrow()来显式声明这个预期,而不是依赖测试默认通过的机制。
2. 这两个测试是否应该合并为一个?
我的建议是要么合并成一个符合AAA规范的完整测试,要么调整第一个测试让它有实际意义,而不是保留一个无断言的空测试。
方案一:合并成一个测试(更推荐)
按照Arrange-Act-Assert模式重构,让测试逻辑完整且独立:
@Test public void whenDeletingExistingRfxCriteria_thenRecordIsRemovedFromDatabase() { // Arrange: 确保要删除的记录存在(如果前面的测试没准备,这里自己创建) RfxCriteri rfxCriteriEconomico = rfxCriteriRepository.save(new RfxCriteri(...)); // Act: 执行删除操作 rfxCriteriRepository.delete(rfxCriteriEconomico.getIdrfxcriteri()); // Assert: 验证记录已被删除 RfxCriteri deletedRecord = rfxCriteriRepository.findByIdrfxcriteri(rfxCriteriEconomico.getIdrfxcriteri()); Assertions.assertNull(deletedRecord); }
这样一个测试就完整覆盖了“删除存在的记录”这个场景,逻辑清晰,也避免了测试之间的顺序依赖(你现在用@Order会让测试耦合,万一前面的测试失败,这个测试也会跟着失败)。
方案二:保留两个独立测试(但要优化第一个)
如果你真的想拆分,那第一个测试应该测试另一个有意义的场景,比如删除不存在的记录是否符合预期:
@Test public void whenDeletingNonExistentRfxCriteria_thenNoExceptionIsThrown() { // Act & Assert: 验证删除不存在的ID不会抛出异常(根据你的业务需求调整,如果应该抛异常就改断言) Assertions.assertDoesNotThrow(() -> rfxCriteriRepository.delete(9999L)); } @Test public void afterDeletingExistingRfxCriteria_thenRecordCannotBeFound() { // Arrange RfxCriteri rfxCriteriEconomico = rfxCriteriRepository.save(new RfxCriteri(...)); rfxCriteriRepository.delete(rfxCriteriEconomico.getIdrfxcriteri()); // Act & Assert RfxCriteri deletedRecord = rfxCriteriRepository.findByIdrfxcriteri(rfxCriteriEconomico.getIdrfxcriteri()); Assertions.assertNull(deletedRecord); }
这样两个测试各有明确的验证目标,而不是一个无意义的“执行不抛异常”测试。
3. 实用的单元/集成测试指南推荐
这里给你几个行业通用的、无需外链就能落地的核心准则:
- 遵循Arrange-Act-Assert (AAA)模式:每个测试都分成三个清晰的部分:准备测试数据/环境(Arrange)、执行要测试的操作(Act)、验证结果是否符合预期(Assert)。这是让测试可读性和维护性翻倍的基础。
- 单一职责原则:每个测试只验证一个场景或行为。比如不要在一个测试里同时测试“删除成功”和“删除不存在的记录”,拆分后每个测试的目标更明确,失败时也更容易定位问题。
- 测试命名要“自文档化”:用类似
when[场景]_then[预期结果]的命名方式,比如whenDeletingExistingRecord_thenRecordIsRemoved,不用看代码就知道这个测试在测什么。 - 消除测试顺序依赖:尽量让每个测试都能独立运行,不要依赖其他测试创建的数据。比如每个测试自己创建需要的记录,测试结束后清理(可以用
@AfterEach或者事务回滚)。依赖@Order的测试维护成本很高,一旦测试顺序变动就会失败。 - 不要测试框架代码:你不需要测试Spring Data的
delete()或findById()方法本身——这些是框架开发者要测试的。你要测试的是你的业务逻辑,比如删除前的权限校验、关联数据的级联处理、删除后的事件发布等。
内容的提问来源于stack exchange,提问作者Marco Sulla

