如何在Kotlin的JUnit 5中针对特定异常实现类/方法级别的测试重试
JUnit 5 Kotlin 重试机制实现方案
要实现你需要的重试逻辑,得用JUnit 5的扩展机制(Extension),配合自定义注解来实现类/方法级别的重试控制。下面是完整的Kotlin实现步骤:
1. 定义自定义重试注解
创建@RetryMechanism注解,同时支持类和方法级别,运行时可读取:
import org.junit.jupiter.api.extension.ExtendWith import kotlin.reflect.KClass @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION) @Retention(RetentionPolicy.RUNTIME) @ExtendWith(RetryExtension::class) annotation class RetryMechanism( val retryOn: KClass<out Throwable>, val retries: Int = 3 // 默认重试3次,可自定义 )
2. 实现重试逻辑扩展
编写RetryExtension类,继承JUnit 5的TestExecutionExceptionHandler,处理异常捕获和重试逻辑:
import org.junit.jupiter.api.extension.ExtensionContext import org.junit.jupiter.api.extension.TestExecutionExceptionHandler import java.util.concurrent.atomic.AtomicInteger class RetryExtension : TestExecutionExceptionHandler { override fun handleTestExecutionException(context: ExtensionContext, throwable: Throwable) { // 优先读取方法注解,无则读取类注解,都没有直接抛出异常 val retryConfig = context.element ?.getAnnotation(RetryMechanism::class.java) ?: context.testClass ?.getAnnotation(RetryMechanism::class.java) ?: throw throwable // 用ExtensionContext存储重试次数,保证线程安全 val retryCount = context.getStore(ExtensionContext.Namespace.create(javaClass, context)) .getOrComputeIfAbsent("retryCount", { AtomicInteger(0) }, AtomicInteger::class.java) if (retryCount.get() < retryConfig.retries && retryConfig.retryOn.isInstance(throwable)) { retryCount.incrementAndGet() println("测试失败,触发重试(第${retryCount.get()}/${retryConfig.retries}次),异常:${throwable.message}") // 重新执行当前测试 context.testExecutionListenerEngine.execute(context) } else { // 达到重试上限或非指定异常,直接抛出终止测试 throw throwable } } }
3. 使用示例
类级别生效(所有测试方法共享重试规则)
import org.junit.jupiter.api.Test import kotlin.test.assertEquals @RetryMechanism(retryOn = MySpecificException::class, retries = 3) class MyTests { @Test fun testSomething() { assertEquals(x, obj.doX()) } @Test fun testAnotherThing() { // 该方法自动应用类上的重试规则 assertEquals(y, obj.doY()) } }
方法级别生效(仅当前方法应用重试规则)
import org.junit.jupiter.api.Test import kotlin.test.assertEquals class MyTests { @Test @RetryMechanism(retryOn = MySpecificException::class, retries = 3) fun testSomething() { assertEquals(x, obj.doX()) } @Test fun testWithoutRetry() { // 该方法无重试逻辑,抛出异常直接失败 assertEquals(z, obj.doZ()) } }
注意事项
- 重试次数为最多重试N次,加上首次执行,总共最多执行N+1次(比如
retries=3时,最多执行4次) - 方法注解会覆盖类注解的配置
- 仅当抛出
retryOn指定的异常时才会重试,其他异常直接终止测试 - 用
ExtensionContext存储重试次数,支持并行测试的线程安全
内容的提问来源于stack exchange,提问作者Homam Manasra
相关产品推荐
相关产品推荐

