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

如何用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.

1. First, Rule Out Basic Configuration Blunders

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 @WebMvcTest or @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.
2. Fixing NPEs for Spring's Two Common Controller Test Approaches

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());
    }
}
3. Use Your Stack Trace to Pinpoint the Exact Issue

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).

Final Quick Checks
  • If MockMvc itself is null, make sure you're initializing it in a @BeforeEach method (or using @Autowired with @WebMvcTest).
  • If view-related beans are null, double-check your WebConfig to ensure you've registered components like ViewResolver correctly.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 07:18:06