1

I am trying to mock a org.w3c.dom.Document object such that calling XPath.evaluate() should return a defined value, e.g., foo, as below:

Document doc = Mockito.mock(Document.class);    
Mockito.when(XPathUtils.xpath.evaluate("/MyNode/text()", doc, XPathConstants.STRING)).thenReturn("foo");

I shall pass the doc object to the target method that will extract the textual contents of node MyNode as foo.

I have tried mocking nodes and setting in the doc object as follows:

            Node nodeMock = mock(Node.class);
            NodeList list = Mockito.mock(NodeList.class);
            Element element = Mockito.mock(Element.class);
            Mockito.when(list.getLength()).thenReturn(1);
            Mockito.when(list.item(0)).thenReturn(nodeMock);
            Mockito.when(doc.getNodeType()).thenReturn(Node.DOCUMENT_NODE);
            Mockito.when(element.getNodeType()).thenReturn(Node.ELEMENT_NODE);
            Mockito.when(nodeMock.getNodeType()).thenReturn(Node.TEXT_NODE);
            Mockito.when(doc.hasChildNodes()).thenReturn(false);
            Mockito.when(element.hasChildNodes()).thenReturn(true);
            Mockito.when(nodeMock.hasChildNodes()).thenReturn(false);
            Mockito.when(nodeMock.getNodeName()).thenReturn("MyNode");
            Mockito.when(nodeMock.getTextContent()).thenReturn("MyValue");
            Mockito.when(element.getChildNodes()).thenReturn(list);
            Mockito.when(doc.getDocumentElement()).thenReturn(element);

But this is giving error like:

org.mockito.exceptions.misusing.WrongTypeOfReturnValue: String cannot be returned by hasChildNodes() hasChildNodes() should return boolean

Is my approach correct and I am missing just another mock, or should I approach it differently? Please help.

bric3
  • 40,072
  • 9
  • 91
  • 111
Muzammil
  • 417
  • 1
  • 4
  • 20
  • 1
    Rather than trying to mock the DOM, wouldn't it be far simpler just to create a hard-coded XML document (as a string in your test class) that has the structure and data you require? – Ian Roberts Mar 11 '15 at 12:00

2 Answers2

4

Don't mock types you don't own !, it's wrong.

To avoid repeating here's an answer that explains why https://stackoverflow.com/a/28698223/48136

EDIT : What I mean is that the code should have usable builder methods (either in the production or in the test classpath) that should be able to create a real Document whatever the source of that document, but certainly not a mock.

For exemple this factory method or builder could use the DocumentBuilder this way :

class FakeXMLBuilder {

    static Document fromString(String xml) {
        DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
        return dBuilder.parse(new ByteArrayInputStream(xml.getBytes("UTF_8")));
    }
}

Of course this has to be tailored to the need of the project, and this can be customized a lot more. In my current project we have a lot of test builders that can create objects, json, etc. For exemple :

Leg leg      = legWithRandomId().contact("Bob").duration(234, SECONDS).build();
String leg   = legWithRandomId().contact("Bob").duration(234, SECONDS).toJSON();
String leg   = legWithRandomId().contact("Bob").duration(234, SECONDS).toXML();
Whatever leg = legWithRandomId().contact("Bob").duration(234, SECONDS).to(WhateverFactory.whateverFactory());
Community
  • 1
  • 1
bric3
  • 40,072
  • 9
  • 91
  • 111
  • Thanks for your reply. But this does not tell me how to solve my problem. Or are you suggesting I should create an XML file for my testing and forget about mocking? – Muzammil Mar 12 '15 at 09:56
  • Yes that's what I'm suggesting, the code you try to should have usable _builder methods_ that should be able to give you a **real** `Document`, whatever the source, but certainly not a mock. – bric3 Mar 12 '15 at 13:49
0

Use Jsoup.parse() with a test XML String. Something like the following should work, for testing some instance method I've assumed is testClassInstance.readFromConnection(String url):

// Turn a block of XML (static String or one read from a file) into a Document
Document document = Jsoup.parse(articleXml);

// Tell Mockito what to do with the Document
Mockito.when(testClassInstance.readFromConnection(Mockito.any()))
  .thenReturn(document);

I'm used to referring to this as "mocking" a Document, but it's just creating and using a real Document, which is then used in mocking other methods. You can construct the xml version of document however you like, or use its regular setters to manipulate it for testing whatever your code is supposed to do with the file. You could also replace that mess of reading-the-file with a constant string, provided it's short enough to be manageable.

Sarah Messer
  • 3,592
  • 1
  • 26
  • 43