Java 中的模拟文件 - 模拟内容 - 模拟

2022-09-01 01:58:42

我对模拟很陌生,我一直在尝试模拟实际内容(本质上是仅在内存中创建一个虚拟文件),以便任何时候都不会将数据写入磁盘。

我已经尝试过诸如模拟文件之类的解决方案,并模拟尽可能多的我可以弄清楚的属性,然后也使用filewriter/bufferedwriter写入它,但这些方法效果不佳,因为它们需要规范路径。有没有人找到除此或类似解决方案以外的解决方案,但是我处理错了吗?

我一直这样做:

private void mocking(){
    File badHTML = mock(File.class);
    //setting the properties of badHTML
    when(badHTML.canExecute()).thenReturn(Boolean.FALSE);
    when(badHTML.canRead()).thenReturn(Boolean.TRUE);
    when(badHTML.canWrite()).thenReturn(Boolean.TRUE);
    when(badHTML.compareTo(badHTML)).thenReturn(Integer.SIZE);
    when(badHTML.delete()).thenReturn(Boolean.FALSE);
    when(badHTML.getFreeSpace()).thenReturn(0l);
    when(badHTML.getName()).thenReturn("bad.html");
    when(badHTML.getParent()).thenReturn(null);
    when(badHTML.getPath()).thenReturn("bad.html");
    when(badHTML.getParentFile()).thenReturn(null);
    when(badHTML.getTotalSpace()).thenReturn(0l);
    when(badHTML.isAbsolute()).thenReturn(Boolean.FALSE);
    when(badHTML.isDirectory()).thenReturn(Boolean.FALSE);
    when(badHTML.isFile()).thenReturn(Boolean.TRUE);
    when(badHTML.isHidden()).thenReturn(Boolean.FALSE);
    when(badHTML.lastModified()).thenReturn(System.currentTimeMillis());
    when(badHTML.mkdir()).thenReturn(Boolean.FALSE);
    when(badHTML.mkdirs()).thenReturn(Boolean.FALSE);
    when(badHTML.setReadOnly()).thenReturn(Boolean.FALSE);
    when(badHTML.setExecutable(true)).thenReturn(Boolean.FALSE);
    when(badHTML.setExecutable(false)).thenReturn(Boolean.TRUE);
    when(badHTML.setReadOnly()).thenReturn(Boolean.FALSE);

    try {
        BufferedWriter bw = new BufferedWriter(new FileWriter(badHTML));
        /*
          badHTMLText is a string with the contents i want to put into the file, 
          can be just about whatever you want
         */
        bw.append(badHTMLText);
        bw.close();

    } catch (IOException ex) {
        System.err.println(ex);
    }
}

任何想法或指导都将非常有帮助。在此之后的某个地方,我基本上尝试使用另一个类从文件中读取。我会尝试模拟某种输入流,但另一个类不采用输入流,因为它是项目的io处理类。


答案 1

你似乎在追求矛盾的目标。一方面,您试图避免将数据写入磁盘,这在测试中并不是一个坏目标。另一方面,您正在尝试测试 I/O 处理类,这意味着您将使用系统实用程序,这些实用程序假定您将使用本机调用。因此,这是我的指导:File

  • 不要试图嘲笑.只是不要。太多的原生事物依赖于它。File
  • 如果可以的话,将 I/O 处理代码拆分为打开 a 并将其转换为 的一半,以及从 中解析出 HTML 的一半。FileReaderReader
  • 此时,您根本不需要模拟 - 只需构造一个 StringReader 来模拟数据源即可。
  • 虽然这可以很好地处理您的单元测试,但您可能还需要编写一个使用临时文件的集成测试,并确保它读取正确。(感谢布莱斯添加这个提示!

不要害怕重构你的类,使测试更容易,如下所示:

class YourClass {
  public int method(File file) {
    // do everything here, which is why it requires a mock
  }   
}   

class YourRefactoredClass {
  public int method(File file) {
    return methodForTest(file.getName(), file.isFile(),
        file.isAbsolute(), new FileReader(file));
  }   

  /** For testing only. */
  int methodForTest(
      String name, boolean isFile, boolean isAbsolute, Reader fileContents) {
    // actually do the calculation here
  }   
}   

class YourTest {
  @Test public int methodShouldParseBadHtml() {
    YourRefactoredClass yrc = new YourRefactoredClass();
    assertEquals(42, yrc.methodForTest(
        "bad.html", true, false, new StringReader(badHTMLText));
  }   
}   

在这一点上,其中的逻辑非常简单,不值得测试,并且其中的逻辑非常容易访问,您可以对其进行大量测试。methodmethodForTest


答案 2

模拟 I/O 调用的一种方法是(使用 Java 7,它将是 NIO 最终类)将所需的调用包装在你自己的类中并模拟它:java.nio.file.Files

public class FileHelper {

    public Path createDirectory(String directoryName) throws IOException {
        return Files.createDirectory(Paths.get(directoryName));
    }

    public boolean exists(String name) throws IOException {
        return Files.exists(Paths.get(name), LinkOption.NOFOLLOW_LINKS);
    }

}

业务逻辑位于 :ImageManager

FileHelper fileHelperMock = Mockito.mock(new FileHelper());
ImageManager imageManager = new ImageManagerImpl(fileHelperMock);

测试将验证对模拟方法的调用:createDirectory()

imageManager.save("directory");
Mockito.verify(fileHelperMock).createDirectory("directory");

我会在测试驱动开发期间使用此方法,其中我不想用实际的文件管理来污染测试(例如,在每个单元测试的最终块中删除创建的目录/文件)。

然后,我将进行验收测试,涵盖实际文件处理的每个用例。