Hard-To-Mock Objects
What to do with objects, which are hard to mock?
For instance, you are working with org.w3c.dom. Suddenly you need a feature of extracting elements by tagname only from the first level. Since by default method getElementsByTagName returns all the elements from all levels, you decide to write your own method:
public List getChildElementsByTagnameFromFirstLevel(Element parentEl, String tagname) {
List childrenEls = new ArrayList();
NodeList nl = parentEl.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
if (nl.item(i).getNodeName().equals(tagname)) {
childrenEls.add((Element) nl.item(i));
}
}
return childrenEls;
}
In order to test this method, you have to create mocks. Let say we use EasyMock for that:
@Test
public void testGetChildElementsByTagnameFromFirstLevel() {
String TAGNAME = "TAGNAME";
Element n1 = EasyMock.createMock(Element.class);
EasyMock.expect(n1.getNodeName()).andReturn(TAGNAME);
Element n2 = EasyMock.createMock(Element.class);
EasyMock.expect(n2.getNodeName()).andReturn("");
Element n3 = EasyMock.createMock(Element.class);
EasyMock.expect(n3.getNodeName()).andReturn(TAGNAME);
NodeList nl = EasyMock.createMock(NodeList.class);
EasyMock.expect(nl.getLength()).andReturn(3);
EasyMock.expectLastCall().times(4);
EasyMock.expect(nl.item(0)).andReturn(n1);
EasyMock.expect(nl.item(0)).andReturn(n1);
EasyMock.expect(nl.item(1)).andReturn(n2);
EasyMock.expect(nl.item(2)).andReturn(n3);
EasyMock.expect(nl.item(2)).andReturn(n3);
Element el = EasyMock.createMock(Element.class);
EasyMock.expect(el.getChildNodes()).andReturn(nl);
EasyMock.replay(n1, n2, n3, nl, el);
Assert.assertEquals(2, domUtils.getChildElementsByTagnameFromFirstLevel(el, TAGNAME).size());
EasyMock.verify(n1, n2, n3, nl, el);
}
Of course, you may have more advanced test method.
Later on you realize, that you need to use the same method in another class. What would you normally do? Yes, move it to the some sort of utility class. But in that case, you would have this method static. And you would have to repeat the same mocking when testing the both classes. The better idea would be to create and interface DOMUtils with a method List getChildElementsByTagnameFromFirstLevel(Element, String) . Then, create implementation DOMUtilsImpl. And inject DOMUtils into needed classes. In this case you would need to mock only DOMUtils.
March 18th, 2010 - 14:32
Please, use mockito )
March 18th, 2010 - 14:58
getChildElementsByTagnameFromFirstLevel is clearly an util method which belongs in a static util class. I highly doubt you will have several implementations doing this specifical thing differently, which means that you’re only putting it behind an interface for the sake of tests. This is wrong.
I would suggest keeping static methods static.
Regarding your point about the duplication of test data: in my opinion, mocking isn’t the best way to test code which processes XML – mocks are neither more readable, nor better structured than the XML tree itself. You would be better off creating minimal suitable XML fragments inside the tests. This way tests will loose some of their ‘unit’ aspect, but will gain in stability (as you’ll reduce the number of mocks) and will not force you into illogical design decisions (see http://martinfowler.com/articles/mocksArentStubs.html for more info on behavioral vs stateful tests).
March 18th, 2010 - 17:24
I will take a look at the mockito.
Well, yes. I agree with mocking XML.