如何单元测试文件访问(Java)?

6 java unit-testing

我知道一个好的单元测试永远不应该访问文件系统.所以我也知道,你可以使用Mockito和PowerMock来模拟File类.

但是下面的代码呢:

public ClassLoaderProductDataProvider(ClassLoader classLoader, String tocResourcePath, boolean checkTocModifications) {
    // ...
    this.cl = classLoader;
    tocUrl = cl.getResource(tocResourcePath);
    if (tocUrl == null) {
        throw new IllegalArgumentException("Can' find table of contents file " + tocResourcePath);
    }
    this.checkTocModifications = checkTocModifications;
    toc = loadToc();
    // ...
}

private ReadonlyTableOfContents loadToc() {
    InputStream is = null;
    Document doc;
    try {
        is = tocUrl.openStream();
        doc = getDocumentBuilder().parse(is);
    } catch (Exception e) {
        throw new RuntimeException("Error loading table of contents from " + tocUrl.getFile(), e);
    } finally {
        if (is != null) {
            try {
                is.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
    try {
        Element tocElement = doc.getDocumentElement();
        ReadonlyTableOfContents toc = new ReadonlyTableOfContents();
        toc.initFromXml(tocElement);
        return toc;
    } catch (Exception e) {
        throw new RuntimeException("Error creating toc from xml.", e);
    }
}
Run Code Online (Sandbox Code Playgroud)

该类使用位于tocResource的文件内容初始化它的toc属性.

因此,我想到的第一件事就是创建一个子类,它不会在构造函数中调用super,因此所有文件访问都没有完成.在我自己的构造函数中,然后我插入应该从文件中读取的数据的测试虚拟数据.然后我可以毫无问题地测试其余的课程.

但是,原始类的构造函数代码根本没有测试过.如果有错误怎么办?

Col*_*inD 9

事情就是这样:通常,要进行适当的单元测试工作,您需要为类提供接口而不是具体类,以便您可以灵活地执行不同的测试.看看你的例子,在我看来,你应该把负载加载Document到其他类......负责一个叫做的接口DocumentSource.

那么你的代码根本不依赖于文件系统.它可能看起来像

public SomethingProductDataProvider(DocumentSource source, String tocDocumentName,
                                    boolean checkTocModifications) {
  this.source = source;
  this.tocDocumentName = tocDocumentName;
  this.checkTocModifications = checkTocModifications;
  this.toc = loadToc();
}

private ReadonlyTableOfContents loadToc() {
  Document doc = source.getDocument(tocDocumentName);
  if (doc == null) {
    throw new IllegalArgumentException("Can' find table of contents file " + 
        tocResourcePath);
  }

  try {
    Element tocElement = doc.getDocumentElement();
    ReadonlyTableOfContents toc = new ReadonlyTableOfContents();
    toc.initFromXml(tocElement);
    return toc;
  } catch (Exception e) {
    throw new RuntimeException("Error creating toc from xml.", e);
  }
}
Run Code Online (Sandbox Code Playgroud)

或者,您可以让类在其构造函数中直接使用Document或甚至InputStream直接使用.当然,在某些时候你必须拥有InputStream使用ClassLoader... 来加载资源的实际代码......但是你可以将代码推送到只有这样做的简单代码中.然后很明显,你对该类进行的任何测试都必须使用实际文件......但其他类的测试不会受到影响.

作为旁注,如果类在其构造函数中起作用(例如在这种情况下加载目录),那么它对于类的可测试性来说是一个不好的标志.可能有一种更好的方法来设计这里涉及的类,消除了对它的需要,并且更加可测试,但很难确切地说明这个设计是什么.

您还可以使用各种其他选项,包括使用Guava的InputSupplier接口以及已经过测试的工厂方法(如Resources.newInputStreamSupplier(URL))来获取InputSupplier生产中使用的实例.但关键是始终让您的类依赖于接口,以便您可以在测试中轻松使用替代实现.