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
相关产品推荐
相关产品推荐

