【问题标题】:Mocking DirectoryStream<Path> without mocking iterator possible?模拟 DirectoryStream<Path> 而不模拟迭代器可能吗?
【发布时间】:2018-04-16 12:11:31
【问题描述】:

我有以下代码:

        Map<String, String> fileContentsByName = new HashMap<String, String>();

        try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(directory))
        {
            for (Path path : directoryStream)
            {
                if (Files.isRegularFile(path))
                {
                    fileContentsByName.put(path.getFileName().toString(), new String(Files.readAllBytes(path)));
                }
            }
        }
        catch (IOException e)
        {
        }

我正在尝试测试此方法。我正在使用Powermock 来获得嘲笑的DirectoryStream&lt;Path&gt;。但是,当测试在代码中遇到 for-each 时,它会因 NPE 而崩溃。如何指定 DirectoryStream 中的路径?

我考虑过更改源代码以使用迭代器并模拟 DirectoryStream 的迭代器以提供所需的路径,但我想知道是否有更好的选择?

【问题讨论】:

    标签: java unit-testing junit mocking powermock


    【解决方案1】:

    假设您上面提供的代码 sn-p 是在这样的类中定义的:

    public class DirectoryStreamReader {
    
        public Map<String, String> read(Path directory) {
    
            Map<String, String> fileContentsByName = new HashMap<String, String>();
            try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(directory)) {
                for (Path path : directoryStream) {
                    if (Files.isRegularFile(path)) {
                        fileContentsByName.put(path.getFileName().toString(), new String(Files.readAllBytes(path)));
                    }
                }
            } catch (IOException e) {
            }
    
            return fileContentsByName;
        }
    }
    

    那么下面的测试就会通过:

    @RunWith(PowerMockRunner.class)
    @PrepareForTest({DirectoryStreamReader.class})
    public class DirectoryStreamTest {
    
        @Rule
        public TemporaryFolder folder= new TemporaryFolder();
    
        @Test
        public void canReadFilesUsingDirectoryStream() throws IOException {
            PowerMockito.mockStatic(Files.class);
    
            Path directory = Mockito.mock(Path.class);
            DirectoryStream<Path> expected = Mockito.mock(DirectoryStream.class);
            Mockito.when(Files.newDirectoryStream(Mockito.any(Path.class))).thenReturn(expected);
    
            File fileOne = folder.newFile();
            File fileTwo = folder.newFile();
            Iterator<Path> directoryIterator = Lists.newArrayList(Paths.get(fileOne.toURI()),
                    Paths.get(fileTwo.toURI())).iterator();
    
            Mockito.when(expected.iterator()).thenReturn(directoryIterator);
    
            Mockito.when(Files.isRegularFile(Mockito.any(Path.class))).thenReturn(true);
            Mockito.when(Files.readAllBytes(Mockito.any(Path.class))).thenReturn("fileOneContents".getBytes()).thenReturn("fileTwoContents".getBytes());
    
            Map<String, String> fileContentsByName = new DirectoryStreamReader().read(directory);
    
            Assert.assertEquals(2, fileContentsByName.size());
            Assert.assertTrue(fileContentsByName.containsKey(fileOne.getName()));
            Assert.assertEquals("fileOneContents", fileContentsByName.get(fileOne.getName()));
            Assert.assertTrue(fileContentsByName.containsKey(fileTwo.getName()));
            Assert.assertEquals("fileTwoContents", fileContentsByName.get(fileTwo.getName()));
        }
    }
    

    这里的重点是:

    • 使用 JUnit 的 TemporaryFolder 规则创建和丢弃一些文件以供测试使用
    • 使用 PowerMockito 模拟与 java.nio.file.Files 的所有交互,这是一个最终类,被模拟的方法是静态的,因此需要 PowerMockito
    • mocking a system class 时遵循 PowerMockito 建议,具体而言:
      • 在测试用例的类级别使用@RunWith(PowerMockRunner.class) 注解。
      • 在测试用例的类级别使用@PrepareForTest({ClassThatCallsTheSystemClass.class}) 注解。
      • 使用mockStatic(SystemClass.class)模拟系统类
    • 此测试已通过 Junit 4.12、Mockito 2.7.19 和 PowerMock 1.7.0 验证

    【讨论】:

    • 愚蠢的问题 - 在 DirectoryStream&lt;Path&gt; 上使用 for-each 与在 DirectoryStream&lt;Path&gt; 上调用 iterator 相同吗?
    • 是的,for 循环结构是迭代器的语法糖。
    • 很好地展示了一个工作测试!不过,使用静态导入会更好地阅读。但更重要的是,我建议实际读取包含几个小文件的测试目录的测试会更好,代码更少,不与 SUT 内部耦合,并且不需要模拟库(或两个,在这个案例)。
    • 问题都是关于 如何 模拟 Files 返回的 DirectoryStream 所以我模拟了这些部分,但与 File 实例的交互使用由创建的实际文件TemporaryFolder 规则。重新静态导入;由于测试是在没有导入语句的情况下呈现的,我认为明确静态导入可能有助于避免混淆。如果我为自己编写此代码+测试,我怀疑我只会使用满足所需条件的实际文件,并且我也会使用静态导入:)
    • @glytching 你能回答这个任务吗,它类似于stackoverflow.com/questions/64444434/…
    猜你喜欢
    • 1970-01-01
    • 2014-02-27
    • 2014-11-07
    • 1970-01-01
    • 2020-07-13
    • 2010-12-18
    • 2015-12-15
    • 1970-01-01
    • 2018-12-21
    相关资源
    最近更新 更多