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

Java 17 SpringBoot应用JUnit测试时RetrySynchronizationManager.getContext()为空

SpringBoot重试逻辑JUnit测试报错:RetrySynchronizationManager.getContext()为空

基于Java 17开发的SpringBoot应用中,重试逻辑功能运行正常,但编写JUnit测试用例时触发空指针异常,报错RetrySynchronizationManager.getContext()返回null。

方法代码

@Retryable(maxAttemptsExpression = "${retry.maxRetry}", 
backoff = @Backoff(delayExpression = "${retry.maxInterval}",
            multiplierExpression = "${retry.delayMultiplier}", maxDelayExpression = "${retry.maxDelay}"))
public Session connect() {
    retryCountConnect = RetrySynchronizationManager.getContext().getRetryCount();
    Session sshSession = null;
    try {
        sshSession = sshSessionPool.getSession();
        sshSession.connect();
        return sshSession;
    } catch (Exception e) {
        log.error("Exception occurred during SshSftp connect, retry count:{}, error details:{}",
                retryCountConnect, ExceptionUtils.getStackTrace(e));
        throw new RuntimeException(e);
    }
}

测试用例代码

class ConnectionTest {
    @Mock
    Session sshSession;
    @Mock
    SshSessionPool sshSessionPool;
    @Mock
    MockedStatic<BasicSshSessionPool> basicSshSessionPoolMockedStatic;
    @Mock
    MockedStatic<AopContext> aopContext;
    Channel channel;
    SshSftpConnection sshSftpConnection;

    @BeforeEach
    @SneakyThrows
    void setUp() {
        channel = Mockito.mock(ChannelExec.class);
        MockitoAnnotations.openMocks(this);
        basicSshSessionPoolMockedStatic.when(() -> BasicSshSessionPool.create(anyString(), anyString(), anyString(), anyInt())).thenReturn(sshSessionPool);
        sshSftpConnection = spy(new SshSftpConnection("host", "username", "password", 22));
        when(sshSessionPool.getSession()).thenReturn(sshSession);
        when(sshSession.openChannel(anyString())).thenReturn(channel);
        aopContext.when(AopContext::currentProxy).thenReturn(sshSftpConnection);
    }

    @AfterEach
    void cleanUp() {
        aopContext.close();
        basicSshSessionPoolMockedStatic.close();
    }

    @Test
    void connect() throws Exception {
        doReturn(sshSession).when(sshSessionPool).getSession();
        Session actual = sshSftpConnection.connect();
        verify(sshSession, times(1)).connect();
        assertEquals(sshSession, actual);
    }
}

错误信息

Cannot invoke "org.springframework.retry.RetryContext.getRetryCount()" because the return value of "org.springframework.retry.support.RetrySynchronizationManager.getContext()" is null
java.lang.NullPointerException: Cannot invoke "org.springframework.retry.RetryContext.getRetryCount()" because the return value of "org.springframework.retry.support.RetrySynchronizationManager.getContext()" is null
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

解决建议

  • 确保Spring Retry的AOP代理生效
    当前测试直接调用sshSftpConnection.connect()方法,未通过Spring代理对象触发@Retryable注解的切面,导致RetrySynchronizationManager未初始化上下文。可改用@SpringBootTest加载应用上下文,通过依赖注入获取代理后的Bean调用方法。

  • 手动初始化RetryContext(单元测试场景)
    若不想加载完整Spring上下文,可手动创建RetryTemplate执行目标方法,强制初始化重试上下文:

    @Test
    void connectWithRetry() throws Exception {
        RetryTemplate retryTemplate = new RetryTemplate();
        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
        retryPolicy.setMaxAttempts(3); // 替换为测试用的重试次数
        retryTemplate.setRetryPolicy(retryPolicy);
    
        Session actual = retryTemplate.execute(context -> {
            // 手动同步重试计数到目标对象
            Field retryCountField = SshSftpConnection.class.getDeclaredField("retryCountConnect");
            retryCountField.setAccessible(true);
            retryCountField.set(sshSftpConnection, context.getRetryCount());
            
            return sshSftpConnection.connect();
        });
    
        verify(sshSession, times(1)).connect();
        assertEquals(sshSession, actual);
    }
    
  • 增加空指针防护
    在原方法中对RetrySynchronizationManager.getContext()的返回值做空判断,避免测试或异常场景下触发空指针:

    public Session connect() {
        RetryContext context = RetrySynchronizationManager.getContext();
        retryCountConnect = context != null ? context.getRetryCount() : 0;
        // 后续原有逻辑不变
    }
    
  • 使用Spring Retry测试支持
    引入spring-retry-test依赖,结合@EnableRetry和测试专用注解,让测试环境自动管理重试上下文。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 21:15:46