40

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

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

我一直在这样做:

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

4

3 回答 3

73

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

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

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

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很容易访问,您可以对其进行大量测试。

于 2013-07-17T03:02:44.240 回答
6

One way to mock I/O calls (with Java 7 it would be the NIO final class java.nio.file.Files) is to wrap the needed calls in your own class and mock it:

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

}

The business logic is located in the ImageManager:

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

The test will verify the call to createDirectory() method on your mock:

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

I would use this method during test driven development where I do not want to pollute testing with real file management (e.g. delete created directories/files in a finally block in each unit test).

Then I would have acceptance tests covering every use case with real file handling.

于 2018-05-14T20:41:20.457 回答
0

Another aproach is to generate a temporary file in an @Before block and then delete it in a @After method.

于 2021-07-14T16:37:07.093 回答