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

Spring Boot项目中无法测试SessionRegistry相关功能问题求助

问题根因排查

  • 框架注解混用冲突
    第一个测试类同时使用@SpringBootTest+@MockBean和Mockito的@InjectMocks+MockitoAnnotations.initMocks,两套注入逻辑互相覆盖:@MockBean已经将Mock实例注入到Spring上下文,MockitoAnnotations.initMocks会重新初始化测试类的字段,把registry替换为未初始化的实例,导致调用when时报“非Mock对象”异常,同时业务类中的SessionRegistry也未正确注入,触发空指针。
  • Mock方法参数不匹配
    你打桩getAllSessions时用的入参是自己new的Object object = new Object(),但实际业务代码里调用时传的是authentication.getPrincipal(),两个对象不是同一个实例,Mockito默认按equals匹配,所以打桩不生效,调用时返回空值。
  • 字段注入兼容性问题
    业务类AppLogoutHandler里SessionRegistry是用@Autowired直接注入私有字段,没有提供构造函数/setter方法,部分低版本Mockito对无构造注入的私有字段注入支持有问题,会导致注入失败。

正确解决方案(二选一即可)

方案1:纯单元测试(不启动Spring上下文,执行速度快)

使用MockitoJUnitRunner,不需要Spring相关注解:

@RunWith(MockitoJUnitRunner.class)
public class AppLogoutSuccessHandlerTest {

    @Mock
    SessionRegistry registry;
    
    @InjectMocks
    AppLogoutHandler appLogoutHandler;
    
    @Mock
    Authentication authentication;
    @Mock
    HttpServletRequest request;
    @Mock
    HttpServletResponse response;
    
    @Test
    public void onLogoutSuccessTest() throws Exception {
        // 构造模拟返回值
        List<SessionInformation> informations = new ArrayList<>();
        // 按需添加模拟的SessionInformation
        // 模拟principal,和authentication.getPrincipal()返回的是同一个对象
        Object mockPrincipal = new Object();
        Mockito.when(authentication.getPrincipal()).thenReturn(mockPrincipal);
        Mockito.when(authentication.getDetails()).thenReturn(new Object());
        // 打桩SessionRegistry,用和上面相同的mockPrincipal作为入参
        Mockito.doReturn(informations).when(registry).getAllSessions(mockPrincipal, false);
        
        // 调用测试方法
        appLogoutHandler.onLogoutSuccess(request, response, authentication);
        
        // 补充你的断言逻辑
    }
}

如果还是注入失败,给AppLogoutHandler换成Spring推荐的构造函数注入,兼容所有版本Mockito:

@Component
public class AppLogoutHandler implements LogoutSuccessHandler{
    private final SessionRegistry sessionRegistry;

    // Spring 4.3+ 单构造函数无需加@Autowired
    public AppLogoutHandler(SessionRegistry sessionRegistry) {
        this.sessionRegistry = sessionRegistry;
    }
    // 其余原有逻辑保持不变
}

方案2:Spring集成测试(启动Spring上下文)

使用@SpringBootTest,不要混用Mockito的@InjectMocks和initMocks:

@RunWith(SpringRunner.class)
@SpringBootTest
public class AppLogoutSuccessHandlerTest {

    @MockBean
    SessionRegistry registry;
    
    @Autowired
    AppLogoutHandler appLogoutHandler; // 直接从Spring上下文取业务类,@MockBean会自动替换上下文中的SessionRegistry
    
    @Mock
    Authentication authentication;
    @Mock
    HttpServletRequest request;
    @Mock
    HttpServletResponse response;
    
    @Test
    public void onLogoutSuccessTest() throws Exception {
        // 和方案1保持一致的打桩逻辑,保证入参匹配
        Object mockPrincipal = new Object();
        Mockito.when(authentication.getPrincipal()).thenReturn(mockPrincipal);
        Mockito.when(authentication.getDetails()).thenReturn(new Object());
        Mockito.doReturn(new ArrayList<>()).when(registry).getAllSessions(mockPrincipal, false);
        
        appLogoutHandler.onLogoutSuccess(request, response, authentication);
        // 补充你的断言逻辑
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.14 13:06:04