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

MockMVC测试POST接口预期201却返回404的问题求助

问题:MockMVC测试POST /answers接口返回404错误

原本尝试编写Rest Docs时遇到大量错误,转而进行MockMVC测试,目标是让POST /answers接口返回201状态码,但实际测试始终得到404错误,即使简化控制器代码仅返回201状态码也无法解决。


测试类代码

import com.google.gson.Gson;
import org.junit.jupiter.api.DisplayName;
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.boot.test.mock.mockito.MockBean;
import org.springframework.data.jpa.mapping.JpaMetamodelMappingContext;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import preproject.underdog.answer.dto.answer.AnswerPostDto;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest(value = AnswerController.class)
//@AutoConfigureRestDocs
@MockBean(JpaMetamodelMappingContext.class)
public class AnswerController {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private Gson gson;

    @Test
    @DisplayName("답변 글 작성 테스트")
    void postAnswer() throws Exception {
        AnswerPostDto post = new AnswerPostDto("테스트", 1L, 1L);
        String content = gson.toJson(post);

        ResultActions actions =
                mockMvc.perform(
                        post("/answers")
                                .accept(MediaType.APPLICATION_JSON)
                                .contentType(MediaType.APPLICATION_JSON)
                                .content(content)
                );

        actions
                .andExpect(status().isCreated());
}

控制器类代码

import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import preproject.underdog.answer.dto.answer.AnswerPostDto;
import preproject.underdog.answer.dto.answer.AnswerRespDto;
import preproject.underdog.answer.mapper.AnswerMapper;
import preproject.underdog.answer.service.AnswerService;

import javax.validation.Valid;
import java.time.LocalDateTime;

@RestController
@RequestMapping("/answers")
@Validated
@RequiredArgsConstructor
public class AnswerController {

    private final AnswerService answerService;
    private final AnswerMapper answerMapper;

    @PostMapping
    public ResponseEntity postAnswer(@Valid @RequestBody AnswerPostDto answerPostDto) {

        AnswerRespDto answerRespDto = new AnswerRespDto(1L,"테스트",1L,1L,1L,  LocalDateTime.of(2023, 4, 3, 3, 3, 0),LocalDateTime.of(2023, 4, 3, 3, 3, 0));

        return new ResponseEntity<>(answerRespDto, HttpStatus.CREATED);
    }
}

测试日志

MockHttpServletRequest:
HTTP Method = POST
Request URI = /answers
Parameters = {}
Headers = [Content-Type:"application/json;charset=UTF-8", Accept:"application/json", Content-Length:"49"]
Body = {"content":"테스트","userId":1,"questionId":1}
Session Attrs = {}

Handler:
Type = org.springframework.web.servlet.resource.ResourceHttpRequestHandler

Async:
Async started = false
Async result = null

Resolved Exception:
Type = null

ModelAndView:
View name = null
View = null
Model = null

FlashMap:
Attributes = null

MockHttpServletResponse:
Status = 404
Error message = null
Headers = [Vary:"Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers"]
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []

简化后的控制器代码

@PostMapping
public ResponseEntity postAnswer(@Valid @RequestBody AnswerPostDto answerPostDto) {
return new ResponseEntity<>(HttpStatus.CREATED);
}

问题原因及解决方案

核心问题是测试类与实际控制器类同名(均为AnswerController),导致@WebMvcTest(value = AnswerController.class)加载的是测试类自身,而非实际处理请求的控制器类,因此无法匹配/answers接口路径,返回404。

解决步骤:

  1. 重命名测试类:将测试类名称改为AnswerControllerTest,与控制器类区分开。
  2. 确保导入正确的控制器类:测试类中@WebMvcTest注解指向实际的AnswerController,并确认导入包路径正确。

修改后的测试类示例:

import com.google.gson.Gson;
import org.junit.jupiter.api.DisplayName;
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.boot.test.mock.mockito.MockBean;
import org.springframework.data.jpa.mapping.JpaMetamodelMappingContext;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import preproject.underdog.answer.dto.answer.AnswerPostDto;
// 确保导入实际的控制器类
import preproject.underdog.answer.controller.AnswerController;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest(AnswerController.class)
@MockBean(JpaMetamodelMappingContext.class)
// 测试类名修改为AnswerControllerTest
public class AnswerControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private Gson gson;

    @Test
    @DisplayName("답변 글 작성 테스트")
    void postAnswer() throws Exception {
        AnswerPostDto post = new AnswerPostDto("테스트", 1L, 1L);
        String content = gson.toJson(post);

        ResultActions actions =
                mockMvc.perform(
                        post("/answers")
                                .accept(MediaType.APPLICATION_JSON)
                                .contentType(MediaType.APPLICATION_JSON)
                                .content(content)
                );

        actions.andExpect(status().isCreated());
    }
}

额外检查点:

  • 确认控制器类在Spring Boot的默认扫描包范围内,若不在,可通过@WebMvcTest的basePackages属性指定扫描路径。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 11:40:57