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

如何为返回服务调用的Spring控制器编写JUnit测试并覆盖代码

Spring Boot控制器测试与代码覆盖率提升

问题背景

作为Spring和JUnit测试新手,希望通过测试控制器方法提升代码行覆盖率。目标控制器类如下:

@RestController
@RequestMapping("/")
public class SummaryController {

 private final SummaryService summaryService;

 @Autowired
 public SummaryController(SummaryService summaryService) {
  this.summaryService = summaryService;
 }

 @GetMapping("/summary")
 @ResponseBody
 public List<SummaryObject> getDirectoryList() {
  return summaryService.getSummary();
 }

}

这是一个Spring Boot/React Web应用,点击“summary”页面会加载本地文件目录内容的表格,控制器方法直接返回summaryService.getSummary()的结果。

自行编写的测试代码如下,但测试通过后未覆盖控制器中return summaryService.getSummary();这一行:

@WebMvcTest (TestController.class)
public class TestController {
 List<SummaryObject> summaryObject;

 @Mock
 private SummaryService summaryService;


 @Test
 void testControllerMatch() {
  this.summaryObject = new ArrayList<>();
  this.summaryObject.add(new SummaryObjectModel("file name", "file size", "last modified"));
 
when(SummaryService.getSummary()).thenReturn(summaryObjectModel);

 assertEquals(SummaryObjectModel, SummaryService.getSummary());
 }
}

核心问题分析

你当前的测试仅验证了Mock的SummaryService行为,完全没有调用控制器的getDirectoryList方法,自然不会覆盖控制器中返回Service结果的代码行。要覆盖目标代码,必须模拟HTTP请求触发控制器方法的执行。

解决方案

1. 编写正确的控制器测试(覆盖目标代码行)

使用MockMvc模拟HTTP请求,调用控制器的/summary接口,验证控制器正确返回Service的数据。修正后的测试代码如下:

@WebMvcTest(SummaryController.class)
public class SummaryControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private SummaryService summaryService;

    @Test
    void getDirectoryList_ShouldReturnSummaryList() throws Exception {
        // 构造测试用的SummaryObject列表
        List<SummaryObject> mockSummaryList = new ArrayList<>();
        mockSummaryList.add(new SummaryObject("file1.txt", "10KB", "2024-05-20"));

        // 配置Mock Service的返回值
        when(summaryService.getSummary()).thenReturn(mockSummaryList);

        // 模拟GET请求并验证响应
        mockMvc.perform(get("/summary")
                        .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk()) // 验证响应状态码为200
                .andExpect(jsonPath("$", hasSize(1))) // 验证返回列表长度为1
                .andExpect(jsonPath("$[0].fileName").value("file1.txt")) // 验证第一个对象的fileName属性
                .andExpect(jsonPath("$[0].fileSize").value("10KB")) // 验证fileSize属性
                .andExpect(jsonPath("$[0].lastModified").value("2024-05-20")); // 验证lastModified属性
    }
}

此测试会触发控制器的getDirectoryList方法执行,从而覆盖return summaryService.getSummary();这一行代码。

2. 验证方法确实返回SummaryObject列表

除了通过jsonPath验证字段,还可以将响应JSON转换为对象列表,直接断言类型和内容:

@Test
void getDirectoryList_ShouldReturnValidSummaryObjectList() throws Exception {
    List<SummaryObject> mockSummaryList = new ArrayList<>();
    mockSummaryList.add(new SummaryObject("file1.txt", "10KB", "2024-05-20"));
    when(summaryService.getSummary()).thenReturn(mockSummaryList);

    // 执行请求并获取响应结果
    MvcResult result = mockMvc.perform(get("/summary")
                        .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andReturn();

    // 将JSON响应转换为对象列表
    String jsonResponse = result.getResponse().getContentAsString();
    ObjectMapper objectMapper = new ObjectMapper();
    List<SummaryObject> responseList = objectMapper.readValue(
        jsonResponse, 
        new TypeReference<List<SummaryObject>>() {}
    );

    // 断言返回结果的类型和内容
    assertThat(responseList).isInstanceOf(List.class);
    assertThat(responseList.get(0)).isInstanceOf(SummaryObject.class);
    assertThat(responseList).isEqualTo(mockSummaryList);
}

3. 针对该方法可扩展的其他测试

  • 空列表场景测试:验证当Service返回空列表时,控制器正确返回空数组
@Test
void getDirectoryList_ShouldReturnEmptyListWhenServiceHasNoData() throws Exception {
    when(summaryService.getSummary()).thenReturn(Collections.emptyList());

    mockMvc.perform(get("/summary")
                    .contentType(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$", hasSize(0)));
}
  • Service异常场景测试:验证当Service抛出异常时,控制器的错误响应逻辑(需根据实际异常处理配置调整断言)
@Test
void getDirectoryList_ShouldReturnErrorWhenServiceFails() throws Exception {
    when(summaryService.getSummary()).thenThrow(new RuntimeException("加载目录失败"));

    mockMvc.perform(get("/summary")
                    .contentType(MediaType.APPLICATION_JSON))
            .andExpect(status().isInternalServerError());
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 14:20:22