首页 > 解决方案 > 在单元测试中的 Java 中,使用 Mockito,如何模拟对 XML DocumentBuilder 的调用,使其不返回 FileNotFoundException

问题描述

我想对一个函数进行单元测试,该函数除其他外调用另一个从磁盘读取 XML 文件的私有函数。在单元测试中,我不想依赖磁盘上的真实文件,而是想使用保存在 String 变量中的 XML。

我尝试同时模拟DocumentBuilderand DocumentBuilderFactory,但没有成功,因为DocumentBuilder仍然想从磁盘读取文件,因此在单元测试中给出FileNotFoundException.

我想避免使用 PowerMockito。

下面是一个简短的代码片段,我可以用它来重现这个问题。

正在测试的类:

public class XMLReader {

    private void readXml() throws IOException {
        DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder docBuilder;
        try {
            docBuilder = docFactory.newDocumentBuilder();
            Document doc = docBuilder.parse("path/to/xml");
        } catch (ParserConfigurationException | SAXException e) {}
    }

    public boolean functionForTest() throws IOException {
        readXml();
        return true;
    }

}

这是我的测试课:

public class AppTest {

    private static final String CLUSTER_CONF_XML = "<element></element>";

    private static Document buildXmlFromString(String xml) throws ParserConfigurationException, IOException, SAXException {
        DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
        return dBuilder.parse(new ByteArrayInputStream(xml.getBytes(Charset.defaultCharset())));
    }

    @org.junit.Test
    public void testFunctionForTest() throws ParserConfigurationException, SAXException, IOException {
        DocumentBuilderFactory documentBuilderFactory = Mockito.mock(DocumentBuilderFactory.class);
        DocumentBuilder xmlDocumentBuilder = Mockito.mock(DocumentBuilder.class);

        try {
            Mockito.when(documentBuilderFactory.newDocumentBuilder()).thenReturn(xmlDocumentBuilder);
            Document doc = buildXmlFromString(CLUSTER_CONF_XML);
            Mockito.when(xmlDocumentBuilder.parse(Mockito.anyString())).thenReturn(doc);
        } catch (IOException e) {}

        XMLReader xmlReader = new XMLReader();
        boolean result = xmlReader.functionForTest();
        assertEquals(result, true);

    }
}

在运行我收到的测试时FileNotFoundException,而预期的行为会模拟 XML,其中 XML 的内容保存在进行模拟时在测试中使用的 String 变量中,从而避免异常和测试用例通过。

预先感谢您的帮助。

标签: javaunit-testingjunitmockito

解决方案


您的问题非常简单:您的实例XMLReader是从“真实”依赖项中调用方法,而不是从您制作的模拟中调用方法。你忘了“注入模拟”。

我会建议一个简单(虽然不干净)的解决方案,但您应该考虑重新设计可测试性:

  1. 重构您的XMLReader,以便它接收DocumentBuilder作为构造函数的参数的依赖项。
  2. DocumentBuilder此外,不要在您的方法上实例化 new,而是readXml()使用您通过构造函数注入的依赖项的引用。
  3. 最后,当您XMLReader在测试用例上实例化时,使用注入我提到的依赖项的构造函数(使用您刚刚制作的模拟)。然后,每当从该readXml()方法进行调用时,它都会从模拟中调用。

例子:

public class XMLReader {

    private DocumentBuilder documentBuilder;

    private void readXml() throws IOException {
        try {
            Document doc = docBuilder.parse("path/to/xml");
        } catch (ParserConfigurationException | SAXException e) {}
    }

    public boolean functionForTest() throws IOException {
        readXml();
        return true;
    }

    public XMLReader(DocumentBuilder documentBuilder) {
        this.documentBuilder = documentBuilder;
    }
}

推荐阅读