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

MockMVC测试遇AssertionError:预期状态码201实际404,求修复方案

Hey there! Let's break down this MockMVC assertion error (expected 201, got 404) step by step—this is a super common issue, so we can fix it quickly, plus I'll share some tips to make your MockMVC tests cleaner.

What's Causing the 404 vs 201 Mismatch?

First off, that error spells out the core problem: your test request isn't matching any controller method in your Spring context. Instead of hitting the endpoint that should return a 201 (resource created), it's hitting a dead end and getting a 404 (resource not found). The most common reasons are:

  • Mismatched request path: The URL in your perform(...) call doesn't match the path in your controller's @PostMapping/@GetMapping/etc. annotation. For example, if your controller uses /api/users but your test calls /users, or you misspelled a path variable like /{userId} vs /{id}.
  • Wrong HTTP method: Your controller uses @PostMapping but your test uses get(...), or vice versa. Spring won't match a GET request to a POST endpoint.
  • Controller not loaded in test context: If you're using @WebMvcTest and didn't specify the controller class to scan, or your controller lives in a package outside Spring's default scan range, MockMVC can't find it.
  • Missing/invalid request parameters/body: If your controller method requires a @RequestParam, @PathVariable, or @RequestBody that your test doesn't provide (or provides incorrectly), Spring might fail to match the method and return 404 instead of a more obvious error like 400.
How to Fix It

Let's go through troubleshooting steps in order of likelihood:

  1. Double-check your request path and HTTP method
    Compare your controller code and test code side-by-side. For example, if your controller looks like this:

    @PostMapping("/api/v1/users")
    public ResponseEntity<User> createUser(@RequestBody User user) {
        User savedUser = userService.save(user);
        return ResponseEntity.status(HttpStatus.CREATED).body(savedUser);
    }
    

    Your test must use the exact same path and POST method:

    mockMvc.perform(post("/api/v1/users")
            .contentType(MediaType.APPLICATION_JSON)
            .content(objectMapper.writeValueAsString(testUser)))
            .andExpect(status().isCreated());
    

    Even a tiny typo (like /api/v1/user instead of /users) will cause a 404.

  2. Verify your test context configuration
    If you're using @WebMvcTest, explicitly specify the controller you want to test—this ensures Spring only loads that controller's context, avoiding missing beans or misconfiguration:

    @WebMvcTest(UserController.class)
    class UserControllerTest {
        // Test code here
    }
    

    If you're using @SpringBootTest + @AutoConfigureMockMvc, make sure your controller is in the same package (or subpackage) as your Spring Boot main application class—this is where Spring scans for components by default.

  3. Ensure you're passing required parameters/body

    • For path variables: Use the placeholder syntax to pass values correctly, like get("/api/v1/users/{id}", 1) instead of hardcoding incorrectly.
    • For request bodies: Always set contentType(MediaType.APPLICATION_JSON) and pass a valid JSON string for @RequestBody methods. If the request body can't be parsed, Spring might fail to match the controller method.
  4. Enable debug logging to see what's happening
    Add this to your application-test.properties to get detailed Spring MVC request matching logs:

    logging.level.org.springframework.web=DEBUG
    

    The logs will show exactly which endpoints Spring tried to match your request against, so you can spot why it didn't find the right one.

Simplifying Your MockMVC Tests

Writing MockMVC tests doesn't have to be repetitive—here are my go-to tips:

  1. Extract setup code to @BeforeEach
    Move repeated setup (like initializing test objects, MockMvc, or ObjectMapper) to a @BeforeEach method to keep your test methods clean:

    @WebMvcTest(UserController.class)
    class UserControllerTest {
        @Autowired
        private MockMvc mockMvc;
        
        @Autowired
        private ObjectMapper objectMapper;
        
        private User testUser;
        
        @BeforeEach
        void setUp() {
            testUser = User.builder()
                    .name("Martine")
                    .email("martine@example.com")
                    .build();
        }
    }
    
  2. Use static imports for cleaner code
    Static import the MockMVC builders and matchers to avoid long class names:

    import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
    
    // Now your test code is much cleaner:
    mockMvc.perform(post("/api/v1/users")
            .contentType(APPLICATION_JSON)
            .content(objectMapper.writeValueAsString(testUser)))
            .andExpect(status().isCreated())
            .andExpect(jsonPath("$.name").value(testUser.getName()));
    
  3. Mock dependencies with @MockBean
    When testing controllers, you don't need real services or repositories—use @MockBean to mock their behavior. This makes tests faster, isolated, and easier to control:

    @WebMvcTest(UserController.class)
    class UserControllerTest {
        @MockBean
        private UserService userService;
        
        @Test
        void createUser_returns201() throws Exception {
            // Mock the service to return our test user
            when(userService.save(any(User.class))).thenReturn(testUser);
            
            // Run the test
            mockMvc.perform(post("/api/v1/users")
                    .contentType(APPLICATION_JSON)
                    .content(objectMapper.writeValueAsString(testUser)))
                    .andExpect(status().isCreated());
        }
    }
    
  4. Chain assertions for comprehensive tests
    You can verify multiple things in one test call—like status code, response body fields, and headers—instead of writing separate tests for each:

    mockMvc.perform(post("/api/v1/users")
            .contentType(APPLICATION_JSON)
            .content(objectMapper.writeValueAsString(testUser)))
            .andExpect(status().isCreated())
            .andExpect(header().string("Location", "/api/v1/users/1"))
            .andExpect(jsonPath("$.email").value(testUser.getEmail()));
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 10:35:22