Spring Boot测试中Mock控制器触发NullPointerException问题排查
问题分析与解决:Spring Boot测试Mock控制器引发的NullPointerException
问题场景
创建Spring Boot测试验证某端点,应用处于运行状态,业务层需调用外部端点,因此创建Mock控制器,但测试时抛出NullPointerException。
错误日志
2023-03-19 00:21:22.082 INFO 13788 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring TestDispatcherServlet 'dispatcherServlet' 2023-03-19 00:21:22.082 INFO 13788 --- [nio-8080-exec-1] o.s.t.web.servlet.TestDispatcherServlet : Initializing Servlet 'dispatcherServlet' 2023-03-19 00:21:22.087 DEBUG 13788 --- [nio-8080-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : 1 mappings in 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping' 2023-03-19 00:21:22.091 DEBUG 13788 --- [nio-8080-exec-1] s.w.s.m.m.a.RequestMappingHandlerAdapter : ControllerAdvice beans: none 2023-03-19 00:21:22.094 DEBUG 13788 --- [nio-8080-exec-1] .m.m.a.ExceptionHandlerExceptionResolver : ControllerAdvice beans: none 2023-03-19 00:21:22.095 DEBUG 13788 --- [nio-8080-exec-1] o.s.t.web.servlet.TestDispatcherServlet : enableLoggingRequestDetails='false': request parameters and headers will be masked to prevent unsafe logging of potentially sensitive data 2023-03-19 00:21:22.095 INFO 13788 --- [nio-8080-exec-1] o.s.t.web.servlet.TestDispatcherServlet : Completed initialization in 13 ms 2023-03-19 00:21:22.109 DEBUG 13788 --- [nio-8080-exec-1] o.s.t.web.servlet.TestDispatcherServlet : POST "/blueprint/callback", parameters={} 2023-03-19 00:21:22.116 DEBUG 13788 --- [nio-8080-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.vmware.tango.integration.mock.MockBlueprintController#callback() 2023-03-19 00:21:22.116 DEBUG 13788 --- [nio-8080-exec-1] o.s.t.web.servlet.TestDispatcherServlet : Failed to complete request: java.lang.NullPointerException 2023-03-19 00:21:22.119 ERROR 13788 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause java.lang.NullPointerException: null at org.springframework.test.web.servlet.TestDispatcherServlet.getHandler(TestDispatcherServlet.java:125) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1047) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:964) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909) at javax.servlet.http.HttpServlet.service(HttpServlet.java:696) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:72) at javax.servlet.http.HttpServlet.service(HttpServlet.java:779) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:214) at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:186) at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354) at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:177) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:360) at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:891) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1784) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.base/java.lang.Thread.run(Thread.java:829)
相关代码
测试类
@Log4j2 @ExtendWith(SpringExtension.class) @SpringBootTest(classes = { WebSecurityConfig.class, WebConfiguration.class }, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @AutoConfigureMockMvc @AutoConfigureWebClient public class WorkflowTest { }
WebConfiguration类
@Configuration public class WebConfiguration implements WebMvcConfigurer { @Bean public ServletWebServerFactory servletWebServerFactory() { return new TomcatServletWebServerFactory(); } }
WebSecurityConfig类
@EnableWebSecurity @Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/**").permitAll() .and() .csrf().disable(); } @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/**"); } }
注:测试类位于独立模块中
问题原因与解决方法
核心原因
日志显示请求已映射到MockBlueprintController#callback(),但TestDispatcherServlet.getHandler()方法触发空指针。主要有两个问题:
- WebSecurity配置过度忽略请求:
web.ignoring().antMatchers("/**")会让Spring Security完全绕过所有请求的处理流程,导致TestDispatcherServlet无法找到合法的处理器映射。 - Mock控制器未被容器扫描:测试类的
@SpringBootTest仅指定了Web相关配置类,未包含MockBlueprintController,导致容器未初始化该控制器实例,映射无效。
解决步骤
修正WebSecurity忽略规则:
移除全局忽略配置,仅忽略静态资源等不需要拦截的路径:@Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/static/**", "/css/**", "/js/**"); }确保Mock控制器被扫描:
在测试类的@SpringBootTest中添加MockBlueprintController类,或者通过@ComponentScan扫描其所在包:// 方式1:直接指定类 @SpringBootTest(classes = { WebSecurityConfig.class, WebConfiguration.class, MockBlueprintController.class }, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) // 方式2:扫描包 @ComponentScan(basePackages = "com.vmware.tango.integration.mock")验证Mock控制器注解:
确保MockBlueprintController添加了@RestController或@Controller,且请求映射注解配置正确:@RestController @RequestMapping("/blueprint") public class MockBlueprintController { @PostMapping("/callback") public ResponseEntity<Void> callback() { // Mock业务逻辑 return ResponseEntity.ok().build(); } }优化测试方式:
若测试运行中的真实应用端点,可使用TestRestTemplate配合随机端口发送请求:@Autowired private TestRestTemplate restTemplate; @LocalServerPort private int port; @Test public void testCallbackEndpoint() { String url = "http://localhost:" + port + "/blueprint/callback"; ResponseEntity<Void> response = restTemplate.postForEntity(url, null, Void.class); assertEquals(HttpStatus.OK, response.getStatusCode()); }
内容的提问来源于stack exchange,提问作者Peter
相关产品推荐
相关产品推荐

