Hip XML testing with Groovy
Verifying XML documents has always been the strength of XMLUnit; however, I’ve often found that this JUnit extension is challenging when trying to verify specific portions of a hip XML document. Sure, XMLUnit supports XPath , but XPath is somewhat of a pain (especially if you aren’t an adept XPather).
For example, given the following copasetic XML document, I’d like to verify the exact value of the name attribute of the first Class element.
<DependencyReport date="Wed Dec 31 21:30:00 EST 1969">
<FiltersApplied>
<Filter pattern="java|org"/>
<Filter pattern="junit."/>
</FiltersApplied>
<Class name="com.vanward.test.MyTest">
<Dependency name="com.vanward.resource.XMLizable"/>
<Dependency name="com.vanward.xml.Element"/>
</Class>
<Class name="com.xom.xml.Test">
<Dependency name="com.vanward.resource.XMLizable"/>
<Dependency name="com.vanward.xml.Element"/>
</Class>
</DependencyReport>
Using XMLUnit and XPath, if you’d like to verify that the first Class element’s name attribute value is com.vanward.test.MyTest, you could use the following disco XPath expression:
//Class[1][@name='com.vanward.test.MyTest']
This XPath expression, in concert with XMLUnit, yields the following JUnit test case that must extend XMLUnit’s XMLTestCase due to the call to assertXpathExists:
public void testToXML() throws Exception{
BatchDependencyXMLReport report =
new BatchDependencyXMLReport(new Date(), this.getFilters());
report.addTargetAndDependencies("com.vanward.test.MyTest",
this.getDependencies());
report.addTargetAndDependencies("com.xom.xml.Test",
this.getDependencies());
assertXpathExists("//Class[1][@name='com.vanward.test.MyTest']",
report.toXML());
}
In order to verify exact portions of an XML document within XMLUnit, I must have a working knowledge of XPath and must extend XMLUnit’s XMLTestClass. Also too, debugging XPath expressions with XMLUnit isn’t exactly easy either– you end up relying on test case successes or failures to ascertain if your expression is actually valid (but what a great way to verify them, I suppose!). Clearly, verifying exact XML values in XMLUnit is possible, but it could be easier.
Using Groovy, traversing XML documents and obtaining values is, in my opinion, amazingly simple and is much more easy to grasp than XPath. For example, verifying the same exact attribute value can be done as follows:
void testToXML() {
def report = new BatchDependencyXMLReport(new Date(), this.getFilters())
report.addTargetAndDependencies("com.vanward.test.MyTest",
this.getDependencies())
report.addTargetAndDependencies("com.xom.xml.Test",
this.getDependencies())
def doc = new XmlSlurper().parseText(report.toXML())
def clazzes = doc.Class
assertEquals("class name should be com.vanward.test.MyTest",
"com.vanward.test.MyTest", clazzes[0].@name.text())
}
In this example, you only need to work with Groovy’s XmlSlurper class and you’re able to traverse XML nodes as if they are a call graph. As you can see, after the XmlSlurper class is initialized with my document, I am able to grab a reference to the collection of Class elements by asking the root node directly by specifying the element name, which, because it’s my bag, is Class.
Once I have this collection of elements, I can simply reference them similar to XPath, but in Groovy’s case, the access method is 0 based (which is array based access that you learned in CS101). If you look at the first XPath example, it’s 1 based (see the [1] reference?). Accessing attributes is also similar to XPath via the @ symbol and obtaining their value requires the text call.
Given these dynomite semantics, the same XML navigation from the first code example is a much more natural clazzes[0].@name.text(). What’s more, the beauty here is that you’re not dealing with Strings either. Granted, the two examples are slightly different– in the pure Java one, due to the limited API of XMLUnit, I’m forced to assert that an expression exists and in Groovy, I can assert that a value exists. The latter feels more flexible to me.
Using Groovy’s innate XML handling ability means XPath isn’t required. Which is easier to comprehend to you– //Class[1][@name='com.vanward.test.MyTest'] or clazzes[0].@name.text()? The next time you find yourself wanting precise access to portions of an XML document, put Groovy to the test– you’ll find it quite groovy. Dig it?
| Related odds and ends | ||
|---|---|---|
Saturday 02 Dec 2006 | Developer Testing, Groovy, JUnit
[...] [...]