如何用JUnit 5测试Spring 5控制器?遇空指针问题求助
Hey there! Let's figure out why you're hitting those annoying NullPointerExceptions when testing your Spring 5 web controller with JUnit 5. Since you already got backend tests working, we just need to tweak your controller test setup to fix those NPEs. Let's break this down step by step.
Most NPEs in controller tests boil down to missing annotations or uninitialized dependencies. Let's start with the obvious checks:
- Did you forget to annotate your test class properly? For JUnit 5 + Spring, you need
@ExtendWith(SpringExtension.class)(though newer Spring versions let you skip this if using@WebMvcTestor@SpringBootTest). - Are you loading the right beans in your test context? If your controller relies on a service or repository, those need to be either mocked or included in the test context.
Spring docs typically cover two main ways to test controllers—let's fix the NPEs for each:
2.1 Standalone MockMvc Setup (Lightweight, Fast)
This approach lets you manually build a MockMvc instance without loading the full Spring context. The #1 cause of NPE here is uninitialized dependencies in your controller.
Example Problem Scenario:
If your controller looks like this:
@Controller public class UserController { @Autowired private UserService userService; // This will be null if not mocked @GetMapping("/users") public String getUsers(Model model) { model.addAttribute("users", userService.getAllUsers()); // NPE here! return "users"; } }
The Fix:
Use Mockito to mock the controller's dependencies and inject them into the controller instance:
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @ExtendWith(MockitoExtension.class) // Enables Mockito support for JUnit 5 public class UserControllerStandaloneTest { @Mock // Creates a mock of UserService private UserService userService; @InjectMocks // Injects the mock UserService into UserController private UserController userController; private MockMvc mockMvc; @BeforeEach void setUp() { // Build MockMvc with our controller (now has a mocked service) mockMvc = MockMvcBuilders.standaloneSetup(userController).build(); } @Test void getUsers_ShouldReturnOkStatus() throws Exception { mockMvc.perform(get("/users")) .andExpect(status().isOk()); } }
No more NPE here because userService is now a mocked instance instead of null.
2.2 Full Spring Context Setup (Integration-Style)
This approach loads a full Spring web context, which is great if you want to test how your controller interacts with other Spring components (like view resolvers or filters). NPEs here usually happen because the context isn't loading the right beans.
For Spring Boot Projects:
Use @WebMvcTest to load only the web-related beans (your controller, MockMvc, etc.) instead of the entire application context:
import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.test.web.servlet.MockMvc; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @WebMvcTest(UserController.class) // Explicitly target your controller public class UserControllerContextTest { @Autowired // MockMvc is auto-configured by @WebMvcTest private MockMvc mockMvc; @Test void getUsers_ShouldReturnOkStatus() throws Exception { mockMvc.perform(get("/users")) .andExpect(status().isOk()); } }
If your controller relies on a service, you can mock it with @MockBean to avoid loading the real service:
@MockBean private UserService userService;
For Pure Spring (Non-Boot) Projects:
You need to explicitly specify your configuration classes and enable the web context:
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = {YourWebConfig.class, UserController.class, UserService.class}) // Load your config and beans @WebAppConfiguration // Required to load a web application context public class UserControllerPureSpringTest { @Autowired private WebApplicationContext webApplicationContext; private MockMvc mockMvc; @BeforeEach void setUp() { mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build(); } @Test void getUsers_ShouldReturnOkStatus() throws Exception { mockMvc.perform(get("/users")) .andExpect(status().isOk()); } }
Take a close look at the stack trace from your IDE—it will tell you exactly which object is null. For example:
java.lang.NullPointerException: Cannot invoke "com.yourpackage.UserService.getAllUsers()" because "this.userService" is null
at com.yourpackage.UserController.getUsers(UserController.java:18)
...
This directly points to userService being null—so you know you need to either mock it (standalone setup) or ensure it's loaded in the test context (full setup).
- If
MockMvcitself is null, make sure you're initializing it in a@BeforeEachmethod (or using@Autowiredwith@WebMvcTest). - If view-related beans are null, double-check your
WebConfigto ensure you've registered components likeViewResolvercorrectly.
内容的提问来源于stack exchange,提问作者Lacunasaurus

