在没有模拟迭代器的情况下模拟DirectoryStream <path>?

时间:2017-11-03 17:20:17

标签: java unit-testing junit mocking powermock

我有以下代码:

        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<Path>。但是,当测试遇到代码中的每一个时,它会随着NPE而爆炸。如何在DirectoryStream中指定路径?

我已经考虑过更改源代码以使用迭代器和模拟DirectoryStream的迭代器来提供所需的路径,但我想知道是否有更好的替代方案?

1 个答案:

答案 0 :(得分:2)

假设您在上面提供的代码段是在如下的类中定义的:

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
  • 验证此测试