如何用Mockito实现Spring Security配置类单元测试的100%覆盖率?
问题描述
我有一个基于Spring Security的配置类SecurityConfiguration,它继承自WebSecurityConfigurerAdapter,代码如下:
@AllArgsConstructor public class SecurityConfiguration extends WebSecurityConfigurerAdapter { private AuthenticationManager authenticationManager; @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests(arc -> arc .antMatchers("/**").authenticated() ) .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .csrf(AbstractHttpConfigurer::disable) .oauth2ResourceServer(rsc -> rsc.opaqueToken( (OAuth2ResourceServerConfigurer<HttpSecurity>.OpaqueTokenConfigurer configurer) -> configurer.authenticationManager(authenticationManager) ) ); } }
我尝试用Mockito编写单元测试,但运行后代码未完全覆盖,测试代码如下:
@ExtendWith(MockitoExtension.class) class SecurityConfigurationTest { @Mock private HttpSecurity httpSecurity; @Mock private SessionManagementConfigurer<HttpSecurity> sessionManagementConfigurer; @InjectMocks private SecurityConfiguration securityConfiguration; @Test void testConfigure() throws Exception { when(httpSecurity.authorizeRequests(any(Customizer.class))).thenReturn(httpSecurity); when(httpSecurity.sessionManagement()).thenReturn(sessionManagementConfigurer); when(sessionManagementConfigurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .thenReturn(sessionManagementConfigurer); when(sessionManagementConfigurer.and()).thenReturn(httpSecurity); when(httpSecurity.csrf(any(Customizer.class))).thenReturn(httpSecurity); when(httpSecurity.oauth2ResourceServer(any(Customizer.class))).thenReturn(httpSecurity); securityConfiguration.configure(httpSecurity); verify(httpSecurity, times(1)).authorizeRequests(any(Customizer.class)); verify(httpSecurity, times(1)).sessionManagement(); verify(sessionManagementConfigurer, times(1)) .sessionCreationPolicy(SessionCreationPolicy.STATELESS); verify(sessionManagementConfigurer, times(1)).and(); verify(httpSecurity, times(1)).csrf(any(Customizer.class)); verify(httpSecurity, times(1)).oauth2ResourceServer(any(Customizer.class)); } }
实现100%代码覆盖率的方法
当前测试仅验证了顶层方法调用,未覆盖lambda表达式内部的逻辑。需要捕获这些lambda并执行,同时验证内部的配置操作:
- 新增必要的Mock对象:为配置链中涉及的
AuthorizationManagerRequestMatcherRegistry、CsrfConfigurer、OAuth2ResourceServerConfigurer、OpaqueTokenConfigurer创建Mock,用于捕获lambda内部的调用。 - 捕获并执行lambda:使用
ArgumentCaptor捕获每个Customizer实例,手动执行其customize方法,触发内部配置逻辑。 - 验证lambda内部的具体操作:确保每个配置细节(如路径匹配、认证要求、csrf禁用、token配置等)都被验证。
修改后的完整测试代码:
@ExtendWith(MockitoExtension.class) class SecurityConfigurationTest { @Mock private HttpSecurity httpSecurity; @Mock private SessionManagementConfigurer<HttpSecurity> sessionManagementConfigurer; @Mock private AuthorizationManagerRequestMatcherRegistry authorizationRegistry; @Mock private CsrfConfigurer<HttpSecurity> csrfConfigurer; @Mock private OAuth2ResourceServerConfigurer<HttpSecurity> oauth2ResourceServerConfigurer; @Mock private OAuth2ResourceServerConfigurer<HttpSecurity>.OpaqueTokenConfigurer opaqueTokenConfigurer; @Mock private AuthenticationManager authenticationManager; @InjectMocks private SecurityConfiguration securityConfiguration; @Test void testConfigure() throws Exception { // 捕获authorizeRequests的Customizer并模拟返回 ArgumentCaptor<Customizer<AuthorizationManagerRequestMatcherRegistry>> authCaptor = ArgumentCaptor.forClass(Customizer.class); when(httpSecurity.authorizeRequests(authCaptor.capture())).thenReturn(httpSecurity); when(authorizationRegistry.antMatchers("/**")).thenReturn(authorizationRegistry); when(authorizationRegistry.authenticated()).thenReturn(authorizationRegistry); // 模拟sessionManagement相关调用链 when(httpSecurity.sessionManagement()).thenReturn(sessionManagementConfigurer); when(sessionManagementConfigurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .thenReturn(sessionManagementConfigurer); when(sessionManagementConfigurer.and()).thenReturn(httpSecurity); // 捕获csrf的Customizer并模拟返回 ArgumentCaptor<Customizer<CsrfConfigurer<HttpSecurity>>> csrfCaptor = ArgumentCaptor.forClass(Customizer.class); when(httpSecurity.csrf(csrfCaptor.capture())).thenReturn(httpSecurity); // 捕获oauth2ResourceServer的Customizer并模拟嵌套调用 ArgumentCaptor<Customizer<OAuth2ResourceServerConfigurer<HttpSecurity>>> oauthCaptor = ArgumentCaptor.forClass(Customizer.class); when(httpSecurity.oauth2ResourceServer(oauthCaptor.capture())).thenReturn(httpSecurity); ArgumentCaptor<Customizer<OAuth2ResourceServerConfigurer<HttpSecurity>.OpaqueTokenConfigurer>> opaqueTokenCaptor = ArgumentCaptor.forClass(Customizer.class); when(oauth2ResourceServerConfigurer.opaqueToken(opaqueTokenCaptor.capture())).thenReturn(oauth2ResourceServerConfigurer); when(opaqueTokenConfigurer.authenticationManager(authenticationManager)).thenReturn(opaqueTokenConfigurer); // 执行配置方法 securityConfiguration.configure(httpSecurity); // 手动执行捕获的lambda,触发内部配置逻辑 authCaptor.getValue().customize(authorizationRegistry); csrfCaptor.getValue().customize(csrfConfigurer); oauthCaptor.getValue().customize(oauth2ResourceServerConfigurer); opaqueTokenCaptor.getValue().customize(opaqueTokenConfigurer); // 验证顶层方法调用 verify(httpSecurity, times(1)).authorizeRequests(any(Customizer.class)); verify(httpSecurity, times(1)).sessionManagement(); verify(sessionManagementConfigurer, times(1)) .sessionCreationPolicy(SessionCreationPolicy.STATELESS); verify(sessionManagementConfigurer, times(1)).and(); verify(httpSecurity, times(1)).csrf(any(Customizer.class)); verify(httpSecurity, times(1)).oauth2ResourceServer(any(Customizer.class)); // 验证lambda内部的具体配置细节 verify(authorizationRegistry, times(1)).antMatchers("/**"); verify(authorizationRegistry, times(1)).authenticated(); verify(csrfConfigurer, times(1)).disable(); verify(oauth2ResourceServerConfigurer, times(1)).opaqueToken(any(Customizer.class)); verify(opaqueTokenConfigurer, times(1)).authenticationManager(authenticationManager); } }
关键说明
- 通过
ArgumentCaptor捕获每个配置环节的Customizer实例,手动调用customize方法确保lambda内部代码被执行。 - 新增了配置链中所有需要的Mock对象,覆盖了所有层级的调用。
- 不仅验证顶层方法调用,还验证了每个具体的配置规则,确保所有代码分支都被覆盖。
内容的提问来源于stack exchange,提问作者Mykola Demchyna
相关产品推荐
相关产品推荐

