单元测试如何mock repository.findAll()后的stream/map/filter链式调用
核心结论
你不需要单独Mock stream()、map{}、filter{} 这类链式调用,whenever(productRepository.findAll()).thenReturn(测试数据)的思路是完全可行的,只需要修正写法细节、构造符合要求的测试假数据即可。
原理说明
productRepository.findAll()是你代码里的外部依赖调用,这是唯一需要Mock的部分;后续的stream()、map{}、filter{}、toList()都是JDK/Kotlin标准库提供的集合原生方法,会基于你给findAll()Mock的返回值真实执行,不需要额外Mock,强行Mock这类内置方法反而会导致流处理逻辑失效。
正确Mock写法(基于Mockito + mockito-kotlin 常用测试栈)
你原来的写法有两个细节错误:
- 方法引用要加执行括号,是
findAll()不是findAll thenReturn传入的参数必须和findAll()的真实返回类型一致,也就是List<Product>类型的集合,不能传入单个Product对象
参考代码:
// 1. 按测试场景构造测试用的Product实体集合 val testProductList = listOf( Product().apply { id = 1 // 按测试场景填充字段:比如要测试收藏过滤就给userFavorite赋值,要测试年份过滤就给关联的categoryComplete字段赋值 userFavorite = true categoryComplete = CategoryComplete().apply { categoryBase = CategoryBase().apply { modelYear = 2024 } } }, Product().apply { id = 2 userFavorite = false categoryComplete = CategoryComplete().apply { categoryBase = CategoryBase().apply { modelYear = 2023 } } } ) // 2. 仅需Mock findAll方法,返回构造好的测试集合 whenever(productRepository.findAll()).thenReturn(testProductList) // 3. 调用待测试的服务方法 val actualResult = productService.getProductList(testUserId, testFilter) // 4. 对返回结果做断言即可,map、filter逻辑会基于测试集合真实执行
常见问题排查
- 如果执行测试时出现空指针,优先检查你构造的测试实体的关联字段是否填充:比如过滤逻辑里用到了
it.categoryComplete?.categoryBase?.modelYear,如果测试实体里相关字段为null,就会走到空值跳过的分支,这不是Mock失效,是测试数据不符合场景。 - 如果要测试无数据抛异常的分支,直接给
findAll()Mock返回emptyList()即可,调用服务方法时会正常触发ProductNoContentException。 - 不要尝试Mock Stream实例或者map、filter这类标准库方法:这类方法是JVM内置实现,Mock后会打断真实的流处理逻辑,导致返回结果不符合预期。
内容的提问来源于stack exchange,提问作者Wellington Mazoni
相关产品推荐
相关产品推荐

