qinghuan
← Java 执行与测试边界目录

JAVA EXECUTION / 补充讲义 2 / 3

测试事务的提交路径与线程边界

区分提交标志、事务结束和提交后效果,并用同线程与抢占式超时对照验证测试回滚范围。

测试方法结束后数据被回滚,并不能证明提交路径正确。验收提交后缓存、事件和异步工具时,要把业务方法返回、事务完成和提交后效果分开观察。

前置:事务与事件边界。下面是未运行的 Java 集成测试设计,需要实际的事务管理器与 Spring TestContext 配置,纯 mock 不足以证明提交。

默认回滚与线程绑定

Spring 测试管理事务(test-managed transaction)默认在测试结束后回滚,不能据此证明提交后的行为正确。测试事务状态绑定当前线程;抢占式超时若在新线程执行测试代码,相关操作不受原测试事务的回滚保护。JUnit Jupiter 的 assertTimeoutPreemptively 属于官方明确提醒的场景;“测试已回滚”不能作为所有线程写入均已清理的证据。

TestTransaction.flagForCommit() 只设置提交标志,不会立即结束事务;end() 才按标志结束当前测试事务。需要事务结束后检查时,可使用 @AfterTransaction;@AfterEach 在正常事务型测试中仍属于测试事务内。区分方法返回、事务完成、提交后效果这三个观察点,避免只断言方法未抛异常。

练习一 显式走到提交完成

仅使用一次性测试数据库、独立假商户 ID 和可计数的同步 AFTER_COMMIT 监听器。两组测试都经代理调用同一 REQUIRED 服务写入并发布事件;组 A 保持默认回滚,组 B 调用 flagForCommit() 后再调用 end()。分别在事务完成后用数据库查询和监听计数断言:A 无持久行且提交后计数为 0;B 有持久行且计数为 1。B 在 end() 前不得把提交标志误判为已提交。提交产生的测试数据用显式清理事务删除并验证,不能依赖当前测试后续自动回滚。

练习二 对照同线程与新线程

在隔离数据库中对照普通同线程调用与 assertTimeoutPreemptively 内调用,两者都调用独立 Spring Bean 的公开 REQUIRED 写入方法,让方法正常完成并返回,不故意制造超时。记录测试线程和服务线程身份;在测试事务结束后检查数据。同线程写入随测试回滚,新线程上的服务会建立自身事务,其正常提交的行可能保留。为每组使用不同 ID,并在 finally/事务外清理中显式删除;这验证的是线程边界,不是超时能否中断数据库操作。若使用其他超时配置,先核实其线程执行模式,不笼统断言所有超时都会换线程。

应提交的记录

记录测试线程与服务线程、实际事务配置、事务结束后的独立查询结果和监听计数。flagForCommit() 后尚未调用 end() 的观察值,不能填写为提交完成结果。提交的测试数据必须显式清理;依赖版本与数据库行为需在自己的隔离环境核对。

来源与版本

Spring Framework 7.0.10。资料原核查时间为 2026 年 10 月 27 日 06:08–06:09 JST,独立发布日期未标明。来源:测试事务指南、TestTransaction API。

固定版本实现对照