JUnit5 PER_CLASS并行测试:退款测试提前启动的解决办法
解决方案:用资源锁绑定支付-退款依赖,替代Order批次控制
你的问题根源在于JUnit 5的@Execution(CONCURRENT)与@Order的交互逻辑:@Order仅保证测试方法的启动顺序,而非完成顺序。当你把两个支付测试标记为@Order(1)、两个退款测试标记为@Order(2)时,JUnit会先启动所有@Order(1)的方法(并行执行),随后立刻启动@Order(2)的方法,不会等待@Order(1)的方法全部完成——这就导致refundPayPal()可能在payPayPal()还未生成支付ID时就开始执行。
无需虚拟测试的解决思路是:放弃用@Order做批次划分,转而用@ResourceLock为每一组支付-退款绑定专属的资源锁,通过读写锁机制保证依赖顺序,同时保留并行执行的能力。
代码实现示例
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.parallel.Execution; import org.junit.jupiter.api.parallel.ExecutionMode; import org.junit.jupiter.api.parallel.ResourceLock; import org.junit.jupiter.api.parallel.ResourceLockMode; import static org.junit.jupiter.api.Assertions.assertNotNull; @TestInstance(TestInstance.Lifecycle.PER_CLASS) @Execution(ExecutionMode.CONCURRENT) class PaymentRefundTests { private String creditCardPaymentId; private String paypalPaymentId; // 信用卡支付:持有专属写锁,完成后释放 @Test @ResourceLock(value = "creditCardPaymentResource", mode = ResourceLockMode.WRITE) void payCreditCard() { // 模拟信用卡支付流程,生成支付ID creditCardPaymentId = generateCreditCardPaymentId(); } // PayPal支付:持有专属写锁,与信用卡支付并行执行(锁key不同) @Test @ResourceLock(value = "paypalPaymentResource", mode = ResourceLockMode.WRITE) void payPayPal() { // 模拟PayPal支付流程,生成支付ID paypalPaymentId = generatePayPalPaymentId(); } // 信用卡退款:持有对应资源的读锁,必须等写锁释放(即支付完成)才能执行 @Test @ResourceLock(value = "creditCardPaymentResource", mode = ResourceLockMode.READ) void refundCreditCard() { assertNotNull(creditCardPaymentId, "信用卡支付ID未生成,无法执行退款"); // 模拟信用卡退款流程 processCreditCardRefund(creditCardPaymentId); } // PayPal退款:持有对应资源的读锁,与信用卡退款并行执行(锁key不同) @Test @ResourceLock(value = "paypalPaymentResource", mode = ResourceLockMode.READ) void refundPayPal() { assertNotNull(paypalPaymentId, "PayPal支付ID未生成,无法执行退款"); // 模拟PayPal退款流程 processPayPalRefund(paypalPaymentId); } // 以下为模拟方法,实际业务中替换为真实逻辑 private String generateCreditCardPaymentId() { return "CC-" + System.currentTimeMillis(); } private String generatePayPalPaymentId() { return "PP-" + System.currentTimeMillis(); } private void processCreditCardRefund(String paymentId) { // 退款逻辑 } private void processPayPalRefund(String paymentId) { // 退款逻辑 } }
核心逻辑说明
- 资源锁绑定依赖:每一组支付-退款使用唯一的资源锁key(如
creditCardPaymentResource),支付方法持有写锁(排他锁,同一时间仅能有一个方法操作该资源,但不同key的写锁互不影响),退款方法持有对应key的读锁(必须等写锁释放后才能获取,确保支付完成后再执行退款)。 - 保留并行能力:不同key的锁互不干扰,因此
payCreditCard()与payPayPal()可以并行执行,refundCreditCard()与refundPayPal()也可以并行执行,完全满足你的异步退款并行需求。 - 无需Order控制:依赖关系由资源锁直接保证,不需要
@Order和虚拟测试,逻辑更清晰。
内容的提问来源于stack exchange,提问作者Ubeogesh
相关产品推荐
相关产品推荐

