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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:35:48