Spring中如何测试返回Page对象的@RestController?
测试返回Page对象的Spring @RestController实用方案
嘿,刚好我之前做过类似的Spring Boot分页接口测试,给你分享几种实用的方案,都是日常开发里常用的工具,帮你覆盖不同场景的测试需求~
方法一:用MockMvc做单元测试(推荐快速验证逻辑)
如果你只想测试Controller的逻辑,不需要启动完整的Spring上下文(比如不用连接真实数据库),MockMvc是最佳选择。它可以模拟HTTP请求,直接调用Controller方法,速度快且隔离性好。
示例代码:
@WebMvcTest(ItemController.class) // 只加载Controller相关的Bean class ItemControllerTest { @Autowired private MockMvc mockMvc; @MockBean // 模拟Repository,避免依赖真实数据库 private ItemRepository itemRepository; // 测试默认请求(无搜索参数) @Test void getPagedItems_defaultRequest_returnsFullPage() throws Exception { // 准备模拟的分页数据 List<Item> mockItems = List.of(new Item("item1"), new Item("item2")); Page<Item> mockPage = new PageImpl<>(mockItems, PageRequest.of(0, 20), 100); // 配置MockRepository的行为 when(itemRepository.findAll(any(Pageable.class))).thenReturn(mockPage); // 发送GET请求并断言响应 mockMvc.perform(get("/api/items")) .andExpect(status().isOk()) // 断言状态码200 .andExpect(jsonPath("$.content.length()").value(2)) // 断言内容列表长度 .andExpect(jsonPath("$.totalElements").value(100)) // 断言总元素数 .andExpect(jsonPath("$.pageable.pageNumber").value(0)); // 断言当前页码 } // 测试按名称搜索的场景 @Test void getPagedItems_searchByName_returnsFilteredPage() throws Exception { Page<Item> mockPage = new PageImpl<>(List.of(new Item("apple")), PageRequest.of(0, 20), 1); when(itemRepository.findByNameStartingWith(eq("app"), any(Pageable.class))).thenReturn(mockPage); mockMvc.perform(get("/api/items") .param("searchBy", "name") .param("searchValue", "app")) .andExpect(status().isOk()) .andExpect(jsonPath("$.content[0].name").value("apple")) // 断言搜索结果 .andExpect(jsonPath("$.totalElements").value(1)); } // 测试默认分支(未知的searchBy参数) @Test void getPagedItems_invalidSearchBy_returnsFullPage() throws Exception { Page<Item> mockPage = new PageImpl<>(List.of(new Item("test")), PageRequest.of(0, 20), 1); when(itemRepository.findAll(any(Pageable.class))).thenReturn(mockPage); mockMvc.perform(get("/api/items") .param("searchBy", "invalid") .param("searchValue", "test")) .andExpect(status().isOk()) .andExpect(jsonPath("$.content[0].name").value("test")); } }
方法二:用TestRestTemplate做集成测试(验证完整流程)
如果需要测试完整的请求链路(比如Controller → Service → Repository → 真实数据库),可以用@SpringBootTest启动完整的Spring上下文,搭配TestRestTemplate发送真实的HTTP请求。
示例代码:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) // 随机端口启动服务器 class ItemControllerIntegrationTest { @Autowired private TestRestTemplate restTemplate; @Autowired private ItemRepository itemRepository; // 测试前初始化数据库数据 @BeforeEach void setUp() { itemRepository.deleteAll(); itemRepository.saveAll(List.of( new Item("apple", "red fruit"), new Item("banana", "yellow fruit"), new Item("orange", "orange fruit") )); } // 测试分页参数(page、size) @Test void getPagedItems_withPagination_returnsCorrectPage() { // 发送请求并接收分页响应,注意用ParameterizedTypeReference处理泛型 ResponseEntity<Page<Item>> response = restTemplate.exchange( "/api/items?page=0&size=2", HttpMethod.GET, null, new ParameterizedTypeReference<Page<Item>>() {} ); // 断言响应状态和分页数据 assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); Page<Item> page = response.getBody(); assertThat(page.getContent()).hasSize(2); // 当前页元素数 assertThat(page.getTotalElements()).isEqualTo(3); // 总元素数 assertThat(page.getTotalPages()).isEqualTo(2); // 总页数 } // 测试按描述搜索 @Test void getPagedItems_searchByDescription_returnsFilteredPage() { ResponseEntity<Page<Item>> response = restTemplate.exchange( "/api/items?searchBy=description&searchValue=red", HttpMethod.GET, null, new ParameterizedTypeReference<Page<Item>>() {} ); Page<Item> page = response.getBody(); assertThat(page.getContent()).hasSize(1); assertThat(page.getContent().get(0).getName()).isEqualTo("apple"); } }
几个关键测试点要覆盖
- 不同的
searchBy参数:name、description、无效值(触发default分支) - 分页参数:
page(默认0)、size(默认20)、sort(比如sort=name,asc) searchValue为空的场景(应返回全部分页数据)- Page对象的元数据:
totalElements、totalPages、currentPage、size是否符合预期
内容的提问来源于stack exchange,提问作者Emilio Gumayagay
相关产品推荐
相关产品推荐

