You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Cucumber测试Spring Boot的@Retryable注解

Spring Boot @Retryable方法的Cucumber集成测试实现

一、基础准备

确保项目依赖包含Spring Retry、Cucumber、WireMock,同时在Spring Boot启动类上添加@EnableRetry注解开启重试功能。

二、WireMock场景配置

利用WireMock的Scenario功能模拟两种核心测试场景:

  • 重试后成功:前两次返回500异常,第三次返回200
  • 始终失败:三次请求均返回500

示例配置代码:

// 重试后成功的Stub
stubFor(get(urlEqualTo("/target-api"))
        .inScenario("Retry Success Scenario")
        .whenScenarioStateIs(Scenario.STARTED)
        .willReturn(aResponse().withStatus(500))
        .willSetStateTo("First Attempt Failed"));

stubFor(get(urlEqualTo("/target-api"))
        .inScenario("Retry Success Scenario")
        .whenScenarioStateIs("First Attempt Failed")
        .willReturn(aResponse().withStatus(500))
        .willSetStateTo("Second Attempt Failed"));

stubFor(get(urlEqualTo("/target-api"))
        .inScenario("Retry Success Scenario")
        .whenScenarioStateIs("Second Attempt Failed")
        .willReturn(aResponse().withStatus(200)));

// 始终失败的Stub
stubFor(get(urlEqualTo("/target-api"))
        .inScenario("Retry Fail Scenario")
        .whenScenarioStateIs(Scenario.STARTED)
        .willReturn(aResponse().withStatus(500))
        .willSetStateTo("Failed"));

stubFor(get(urlEqualTo("/target-api"))
        .inScenario("Retry Fail Scenario")
        .whenScenarioStateIs("Failed")
        .willReturn(aResponse().withStatus(500)));

三、重试次数统计的两种可行方案

方案1:基于Spring RetryListener统计

实现Spring Retry提供的RetryListener接口,监听重试过程统计重试次数(不含首次尝试):

@Component
public class RetryCounterListener implements RetryListener {
    private int retryTimes = 0;

    @Override
    public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
        return true;
    }

    @Override
    public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
        retryTimes = context.getRetryCount();
    }

    @Override
    public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {}

    public int getRetryTimes() {
        return retryTimes;
    }

    public void resetCounter() {
        retryTimes = 0;
    }
}

测试时注入该Listener,调用方法后断言retryTimes等于2(对应maxAttempts=3,重试2次)。

方案2:Spring AOP统计总调用次数

通过AOP拦截所有标注@Retryable的方法,统计含首次尝试的总调用次数:

@Component
@Aspect
public class RetryMethodInterceptor {
    private int totalInvokeCount = 0;

    @Pointcut("@annotation(org.springframework.retry.annotation.Retryable)")
    public void retryableMethods() {}

    @Before("retryableMethods()")
    public void countInvocations(JoinPoint joinPoint) {
        totalInvokeCount++;
    }

    public int getTotalInvokeCount() {
        return totalInvokeCount;
    }

    public void resetCounter() {
        totalInvokeCount = 0;
    }
}

maxAttempts=3时,总调用次数为3,直接断言该值即可。

四、Cucumber测试用例编写

场景1:重试后成功

Scenario: 服务调用失败后重试,最终成功
Given WireMock已配置为前两次返回500,第三次返回200
When 调用标注@Retryable的服务方法
Then 方法返回HTTP 200状态码
And 重试次数统计为2次(或总调用次数为3次)

场景2:始终失败

Scenario: 服务调用多次重试后仍失败
Given WireMock已配置为三次都返回500
When 调用标注@Retryable的服务方法
Then 方法抛出指定异常
And 重试次数统计为2次(或总调用次数为3次)

五、关键注意事项

  • 每个测试用例执行前,必须重置统计计数器,避免用例间数据污染
  • 由于设置了delay=1,测试时可借助Awaitility工具等待重试完成,避免断言时机过早
  • WireMock的Scenario在每次测试后要调用resetAllScenarios()重置状态

内容的提问来源于stack exchange,提问作者sejo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 02:10:31