在 Java 中模拟文件 - 模拟内容 - Mockito

2022-01-14 00:00:00 file mockito java

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

I'm pretty new to mocking, and I've been trying to mock the actual contents (essentially create a virtual file in memory alone) so that no data is written to disk at any point.

我尝试了一些解决方案,例如模拟文件并模拟尽可能多的属性,然后使用文件写入器/缓冲写入器写入文件,但这些都不能很好地工作,因为他们需要规范路径.任何人都找到了除此或类似之外的解决方案,但我正在接近这个错误?

I've tried solutions like mocking the file and mocking as many of the properties that I can figure out as much as possible, an then also writing into it with a filewriter/bufferedwriter, but those don't work well, since they need canonical paths. Anyone found a solution other than this or similar, but that I'm approaching this wrong?

我一直是这样的:

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 处理类.

Any ideas or guidance would be very helpful. Somewhere after this i basically try to read from the file using another class. I would try to mock some sort of input stream, but the other class doesn't take an inputstream, since it's the io handling class for the project.

推荐答案

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

You seem to be after contradictory goals. On the one hand, you're trying to avoid writing data to disk, which isn't a bad goal in tests. On the other, you're trying to test your I/O-handling class, which means you'll be working with system utilities that assume that your File will work with native calls. As such, here's my guidance:

  • 不要试图模拟 File.只是不要.太多原生事物依赖它.
  • 如果可以,请将您的 I/O 处理代码分成打开 File 并将其转换为 Reader 的一半,以及解析 HTML 的一半阅读器.
  • 此时,您根本不需要模拟 - 只需构造一个 StringReader 来模拟数据源.
  • 虽然它可以很好地处理您的单元测试,但您可能还想编写一个使用 集成测试temp-file-in-java">临时文件 并确保它读取正确.(感谢 Brice 添加提示!)
  • Don't try to mock a File. Just don't. Too many native things depend on it.
  • If you can, split your I/O-handling code into the half that opens a File and turns it into a Reader, and the half that parses HTML out of the Reader.
  • At that point, you don't need a mock at all--just construct a StringReader to simulate the data source.
  • While that handles your unit tests pretty well, you may also want to write an integration test that uses a temporary file and ensure that it reads right. (Thanks Brice for adding that tip!)

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

Don't be afraid to refactor your class to make testing easier, as here:

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));
  }   
}   

此时 method 中的逻辑非常简单,不值得测试,methodForTest 中的逻辑非常容易访问,您可以对其进行大量测试.

At this point the logic in method is so straightforward it's not worth testing, and the logic in methodForTest is so easy to access that you can test it heavily.

相关文章