如何在Stream流操作中比对两个列表的元素ID完成断言校验
问题解答
首先给出核心结论:你不一定需要单独创建新的Stream流,具体实现可以根据你是否要求列表顺序匹配来选:
方案1:顺序匹配(仅用1个IntStream即可,无需额外处理响应体Stream)
如果你已经确认两个列表长度相等、返回顺序和预期列表顺序完全一致(你这里mock了service返回sampleUsers,正常顺序是对齐的),直接用索引遍历的方式最直观:
先把sampleUsers()的返回值存为本地变量,避免重复调用导致数据不一致,然后实现如下:
@Test void findAllUsers() { List<User> expectedUsers = sampleUsers(); when(userService.findAll()).thenReturn(expectedUsers); ResponseEntity<List<UserDto>> responseEntity = userController.findAll(); // 状态码断言 assertEquals(200, responseEntity.getStatusCode().value()); List<UserDto> actualUsers = responseEntity.getBody(); // 长度断言 assertEquals(expectedUsers.size(), actualUsers.size()); // 按索引逐一校验属性 IntStream.range(0, expectedUsers.size()) .forEach(i -> { UserDto actual = actualUsers.get(i); // 非空校验 assertNotNull(actual.getId()); assertNotNull(actual.getUsername()); // ID比对 assertEquals(expectedUsers.get(i).getId(), actual.getId()); }); }
方案2:顺序匹配(沿用你现有写了一半的响应体Stream)
如果你不想改现有Stream的结构,只需要提前拿到预期ID的迭代器即可,不需要额外开独立的Stream做处理:
Iterator<Long> expectedIdIter = sampleUsers().stream().map(User::getId).iterator(); responseEntity.getBody().stream() .filter(user -> user.getId() != null) .filter(user -> user.getUsername() != null) .forEach(actualUser -> assertEquals(expectedIdIter.next(), actualUser.getId()));
方案3:不要求顺序(需要提取两个列表的ID集合做断言)
如果业务不限制返回列表的顺序,只要求所有ID都存在,你可以分别提取两个列表的ID集合做比对,这种情况需要创建两个Stream分别处理预期和实际列表:
// 提取预期ID集合 Set<Long> expectedIds = sampleUsers().stream() .map(User::getId) .collect(Collectors.toSet()); // 提取实际ID集合,同时做非空校验 Set<Long> actualIds = responseEntity.getBody().stream() .peek(user -> { assertNotNull(user.getId()); assertNotNull(user.getUsername()); }) .map(UserDto::getId) .collect(Collectors.toSet()); // 直接断言两个集合相等 assertEquals(expectedIds, actualIds);
注意事项
- 尽量避免多次调用
sampleUsers(),如果该方法内部是每次生成新的列表,多次调用可能会拿到不同的数据导致断言失败,建议提前把返回值存为本地变量复用。 - 如果需要校验的字段不止ID,建议直接重写
UserDto的equals方法,或者用AssertJ的集合断言方法简化写法,不需要自己写Stream处理。
内容的提问来源于stack exchange,提问作者fat_pikachu
相关产品推荐
相关产品推荐

