Tuesday, June 19, 2007

How to compare XML in unit tests

When writing unit tests that compare basic XML I have historically just kept it simple and used string comparison. Granted it's highly error prone and an extra whitespace, tab, or incorrect order can cause the test to fail, but it's quick and when your test is simple enough it's sufficient. The strategy I tend to take when writing code also extends to my unit tests: start simple and improve the code as you go along. So when string comparison works; use it. When your unit test demands more then perhaps you should look at XMLUnit. It's actually quite simple and helps you avoid that nasty feeling you get when you use .equals().

Your first iteration might look something like this. Here you are expecting the method getResult() to return back '<basic/>'.

String expectedXml = "<basic/>";
String result = xmlBuilder.getResult();

assertEquals(expectedXml, result);

Now lets say getResult() returns back '<basic></basic>'. Now the test will fail even though the XML is equivalent. At first it's tempting to just add another test that expects the new format, and to be honest I seem to recall doing that. But now I have a new tool to use, so here is how you can use XMLUnit to test XML.

First start by adding it as a dependency. If you are a maven2 user add the following to your POM:


<dependencies>
<dependency>
<groupid>xmlunit</groupid>
<artifactid>xmlunit</artifactid>
<version>1.0</version>
<scope>test</scope>
</dependency>
</dependencies>

Next refactor your unit test to extend org.custommonkey.xmlunit.XMLTestCase instead of junit.framework.TestCase.
Finally replace the assertEquals() with assertXMLEqual() and that is it. So now your test would look like:

String expectedXml = "<basic/>";
String result = xmlBuilder.getResult();

assertXMLEqual(expectedXml, result);

And would pass for all combinations of '<basic/>'; such as '<basic></basic>'. As an additional hint you also might want to turn on ignoring whitespace. To accomplish this insert the following line in your code (I placed it in my constructor) to ignore whitespaces:

XMLUnit.setIgnoreWhitespace(true);

Now the other combinations will pass such as: '<basic>   </basic>'.

For more information on XMLUnit see their Java User's Guide.

0 comments: