使用MockMvc测试调用RestTemplate的控制器时返回空,如何解决?
解决Controller单元测试响应为空的问题
问题原因
- 你的测试类中,
MyController被注入了Mock的Myservice实例,Mock对象的方法默认返回null。当Controller调用myservice.getTodo()时,得到的是null,最终返回的响应体就是空字符串。 - 你Mock了
RestTemplate,但这个Mock并没有关联到Myservice上——因为你用的是Mock的Myservice,它的getTodo()方法根本不会执行实际代码,所以RestTemplate的Mock配置完全没用。
方案1:仅测试Controller层(Mock Service)
这种方式专注验证Controller的逻辑,无需关心Service内部调用,直接Stub Service方法返回预期结果即可:
@ExtendWith(SpringExtension.class) class MyControllerTestJunit5 { private MockMvc mockMvc; @Mock private Myservice myservice; @InjectMocks MyController myController; @BeforeEach void setUp() { mockMvc = MockMvcBuilders.standaloneSetup(myController).build(); } @Test public void testGetTodo() throws Exception { // 提前准备好预期的JSON字符串 String expectedJson = "{\"userId\":5,\"id\":6,\"title\":\"myfield\",\"completed\":true}"; // Stub Service的getTodo方法,返回预设的JSON when(myservice.getTodo()).thenReturn(expectedJson); MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/todo")) .andExpect(status().isOk()) .andExpect(content().json(expectedJson)) // 直接验证响应JSON内容 .andReturn(); String response = result.getResponse().getContentAsString(); System.out.println("response: " + response); } }
方案2:测试Controller+Service集成(Mock RestTemplate)
如果需要验证Service调用RestTemplate的逻辑,要让Controller使用真实的Myservice实例,并将Mock的RestTemplate注入到Service中,同时处理Service的ObjectMapper依赖:
第一步:修复Service的依赖注入
@Service public class Myservice { @Autowired private RestTemplate restTemplate; @Autowired private ObjectMapper objectMapper; // 注意抛出序列化异常,避免编译报错 public String getTodo() throws JsonProcessingException { ResponseEntity<Todo> response = restTemplate.exchange( "https://jsonplaceholder.typicode.com/todos/1", HttpMethod.GET, null, Todo.class); Todo todo = response.getBody(); return objectMapper.writeValueAsString(todo); } }
第二步:修改测试代码
@ExtendWith(SpringExtension.class) class MyControllerTestJunit5 { private MockMvc mockMvc; @Mock private RestTemplate restTemplate; @Autowired private ObjectMapper objectMapper; private Myservice myservice; private MyController myController; @BeforeEach void setUp() { // 初始化真实Service,注入Mock的RestTemplate和ObjectMapper myservice = new Myservice(); // 如果是私有字段,可用ReflectionTestUtils.setField(myservice, "restTemplate", restTemplate); myservice.restTemplate = restTemplate; myservice.objectMapper = objectMapper; // 初始化Controller,注入真实Service myController = new MyController(); myController.myservice = myservice; mockMvc = MockMvcBuilders.standaloneSetup(myController).build(); } @Test public void testGetTodo() throws Exception { Todo todoSample = new Todo(5,6,"myfield", true); ResponseEntity<Todo> responseEntity = ResponseEntity.ok(todoSample); // Mock RestTemplate的exchange方法,精准匹配参数 when(restTemplate.exchange( ArgumentMatchers.eq("https://jsonplaceholder.typicode.com/todos/1"), ArgumentMatchers.eq(HttpMethod.GET), ArgumentMatchers.isNull(), ArgumentMatchers.eq(Todo.class))) .thenReturn(responseEntity); String expectedJson = objectMapper.writeValueAsString(todoSample); MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/todo")) .andExpect(status().isOk()) .andExpect(content().json(expectedJson)) .andReturn(); String response = result.getResponse().getContentAsString(); System.out.println("response: " + response); } }
内容的提问来源于stack exchange,提问作者creas443
相关产品推荐
相关产品推荐

