如何使用Mockito模拟Files.walk(Paths.get(dropLocation))以测试静态方法?
问题:Mockito模拟Files.walk静态方法失败的解决办法
待测试的静态方法
public static List<File> getFilesPath(String dropLocation) throws IOException{ try(Stream<Path> streamPath = Files.walk(Paths.get(dropLocation))){ return streamPath .map(Path::toFile) .filter(File::exists) .collect(Collectors.toList()); } }
本人尝试的测试代码(未成功)
@Mock private StepContribution stepContribution; @Mock private ChunkContext chunkContext; @Mock private Stream<Path> streamPath; @Mock private Path path; @Before public void setup() throws IOException { MockitoAnnotations.initMocks(this); PowerMockito.mockStatic(Files.class); //PowerMockito.mockStatic(Paths.class); Path p = Paths.get("Test"); when(p).thenReturn(p); when(Files.walk(path)).thenReturn(streamPath); when(streamPath.count()).thenReturn(100L); tasklet = new HistoricAutoBorrowRequestTasklet(); } @Test public void testTasklet() throws Exception { RepeatStatus repeatStatus = tasklet.execute(stepContribution, chunkContext); }
解决方案
你的测试代码存在几个关键问题,修正后就能正常模拟:
必须同时Mock Paths和Files的静态方法
原方法里先调用Paths.get(dropLocation)获取Path对象,再传给Files.walk,所以要同时对这两个类做静态Mock,不能注释掉PowerMockito.mockStatic(Paths.class)。Mock的对象要对应匹配
你当前模拟Files.walk(path)时用的是@Mock的path对象,但原方法里是用Paths.get(dropLocation)的返回值作为参数传给Files.walk,所以要让Paths.get返回你Mock的Path对象,再用这个对象来匹配Files.walk的调用。要完整模拟Stream的处理流程
原方法对Stream做了map、filter、collect操作,只模拟count()完全不够,需要模拟每一步的返回值,确保最终能收集到预期的列表。处理try-with-resources的Stream关闭
因为原方法用了try-with-resources,Stream会自动调用close(),需要Mock这个方法避免报错。
完整修正后的测试代码
@RunWith(PowerMockRunner.class) @PrepareForTest({Files.class, Paths.class}) // 必须声明要Mock的静态类 public class HistoricAutoBorrowRequestTaskletTest { @Mock private StepContribution stepContribution; @Mock private ChunkContext chunkContext; @Mock private Stream<Path> mockStream; @Mock private Path mockPath; @Mock private File mockFile; private HistoricAutoBorrowRequestTasklet tasklet; @Before public void setup() throws IOException { MockitoAnnotations.initMocks(this); // Mock两个静态类 PowerMockito.mockStatic(Files.class); PowerMockito.mockStatic(Paths.class); // 模拟Paths.get("Test")返回mockPath when(Paths.get("Test")).thenReturn(mockPath); // 模拟Files.walk传入mockPath时返回mockStream when(Files.walk(mockPath)).thenReturn(mockStream); // 模拟Stream的map操作:将mockPath转为mockFile when(mockStream.map(Path::toFile)).thenReturn(Stream.of(mockFile)); // 模拟File.exists()返回true,确保能通过filter when(mockFile.exists()).thenReturn(true); // 模拟collect操作返回预期的列表 when(mockStream.collect(any(Collector.class))).thenReturn(Collections.singletonList(mockFile)); // 处理Stream的close方法,避免try-with-resources报错 doNothing().when(mockStream).close(); tasklet = new HistoricAutoBorrowRequestTasklet(); } @Test public void testTasklet() throws Exception { // 假设tasklet.execute内部调用了getFilesPath("Test") RepeatStatus repeatStatus = tasklet.execute(stepContribution, chunkContext); // 验证静态方法调用是否符合预期 PowerMockito.verifyStatic(Paths.class); Paths.get("Test"); PowerMockito.verifyStatic(Files.class); Files.walk(mockPath); // 验证最终结果符合预期(根据你的业务逻辑调整断言) // 比如如果getFilesPath的返回值被用来做后续操作,可以断言相关行为 } }
注意事项
- 必须使用
PowerMockRunner来运行测试,因为要Mock静态方法。 @PrepareForTest必须包含所有要Mock的静态类(Files和Paths)。- 模拟Stream的链式调用时,要确保每一步的返回值都正确Mock,比如
map之后要返回包含mockFile的Stream,否则后续的filter和collect无法正常执行。
内容的提问来源于stack exchange,提问作者PAA
相关产品推荐
相关产品推荐

