ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 02.01.2026
Просмотров: 488
Скачиваний: 0
XMLUnit Java User’s Guide |
6 / 35 |
1.8XPath Tests
One of the strengths of XML is the ability to programmatically extract specific parts of a document using XPath expressions. The XMLTestCase class offers a number of XPath related assertion methods, as demonstrated in this test:
Example 1.11 Using XPath Tests
public void testXPaths() throws Exception { String mySolarSystemXML = "<solar-system>"
+"<planet name=’Earth’ position=’3’ supportsLife=’yes’/>"
+"<planet name=’Venus’ position=’4’/></solar-system>"; assertXpathExists("//planet[@name=’Earth’]", mySolarSystemXML); assertXpathNotExists("//star[@name=’alpha centauri’]",
mySolarSystemXML); assertXpathsEqual("//planet[@name=’Earth’]",
"//planet[@position=’3’]", mySolarSystemXML); assertXpathsNotEqual("//planet[@name=’Venus’]",
"//planet[@supportsLife=’yes’]", mySolarSystemXML);
}
When an XPath expression is evaluated against a piece of XML a NodeList is created that contains the matching Nodes. The methods in the previous test assertXpathExists, assertXpathNotExists, assertXpathsEqual, and assert XpathsNotEqual use these NodeLists. However, the contents of a NodeList can be flattened (or String-ified) to a single value, and XMLUnit also allows assertions to be made about this single value, as in this test5:
Example 1.12 Testing XPath Values
public void testXPathValues() throws Exception { String myJavaFlavours = "<java-flavours>"
+"<jvm current=’some platforms’>1.1.x</jvm>"
+"<jvm current=’no’>1.2.x</jvm>"
+"<jvm current=’yes’>1.3.x</jvm>"
+"<jvm current=’yes’ latest=’yes’>1.4.x</jvm></javaflavours>"; assertXpathEvaluatesTo("2", "count(//jvm[@current=’yes’])",
myJavaFlavours); assertXpathValuesEqual("//jvm[4]/@latest", "//jvm[4]/@current",
myJavaFlavours); assertXpathValuesNotEqual("//jvm[2]/@current",
"//jvm[3]/@current", myJavaFlavours);
}
XPaths are especially useful where a document is made up largely of known, unchanging content with only a small amount of changing content created by the system. One of the main areas where constant "boilerplate" markup is combined with system generated markup is of course in web applications. The power of XPath expressions can make testing web page output quite trivial, and XMLUnit supplies a means of converting even very badly formed HTML into XML to aid this approach to testing.
The HTMLDocumentBuilder class uses the Swing HTML parser to convert marked-up content to Sax events. The Tole rantSaxDocumentBuilder class handles the Sax events to build up a DOM document in a tolerant fashion i.e. without mandating that opened elements are closed. (In a purely XML world this class would have no purpose as there are plenty of Sax event handlers that can build DOM documents from well formed content). The test below illustrates how the use of these classes:
Example 1.13 Working with non well-formed HTML
public void testXpathsInHTML() throws Exception {
String someBadlyFormedHTML = "<html><title>Ugh</title>"
+"<body><h1>Heading<ul>"
+"<li id=’1’>Item One<li id=’2’>Item Two"; TolerantSaxDocumentBuilder tolerantSaxDocumentBuilder =
5 Each of the assertXpath...() methods uses an implementation of the XpathEngine interface to evaluate an XPath expression.
XMLUnit Java User’s Guide |
7 / 35 |
new TolerantSaxDocumentBuilder(XMLUnit.getTestParser());
HTMLDocumentBuilder htmlDocumentBuilder =
new HTMLDocumentBuilder(tolerantSaxDocumentBuilder); Document wellFormedDocument =
htmlDocumentBuilder.parse(someBadlyFormedHTML); assertXpathEvaluatesTo("Item One", "/html/body//li[@id=’1’]",
wellFormedDocument);
}
One of the key points about using XPaths with HTML content is that extracting values in tests requires the values to be identifiable. (This is just another way of saying that testing HTML is easier when it is written to be testable.) In the previous example id attributes were used to identify the list item values that needed to be testable, however class attributes or span and div tags can also be used to identify specific content for testing.
1.9Testing by Tree Walking
The DOM specification allows a Document to optionally implement the DocumentTraversal interface. This interface allows an application to iterate over the Nodes contained in a Document, or to "walk the DOM tree". The XMLUnit NodeTest class and NodeTester interface make use of DocumentTraversal to expose individual Nodes in tests: the former handles the mechanics of iteration, and the latter allows custom test strategies to be implemented. A sample test strategy is supplied by the CountingNodeTester class that counts the nodes presented to it and compares the actual count to an expected count. The test below illustrates its use:
Example 1.14 Using CountingNodeTester
public void testCountingNodeTester() throws Exception {
String testXML = "<fibonacci><val>1</val><val>2</val><val>3</val>" + "<val>5</val><val>9</val></fibonacci>";
CountingNodeTester countingNodeTester = new CountingNodeTester(4); assertNodeTestPasses(testXML, countingNodeTester, Node.TEXT_NODE);
}
This test fails as there are 5 text nodes, and JUnit supplies the following message:
Expected node test to pass, but it failed! Counted 5 node(s) but expected 4
Note that if your DOM implementation does not support the DocumentTraversal interface then XMLUnit will throw an IllegalArgumentException informing you that you cannot use the NodeTest or NodeTester classes. Unfortunately even if your DOM implementation does support DocumentTraversal, attributes are not exposed by iteration: however they can be examined from the Element node that contains them.
While the previous test could have been easily performed using XPath, there are times when Node iteration is more powerful. In general, this is true when there are programmatic relationships between nodes that can be more easily tested iteratively. The following test uses a custom NodeTester class to illustrate the potential:
Example 1.15 Using a Custom NodeTester
public void testCustomNodeTester() throws Exception {
String testXML = "<fibonacci><val>1</val><val>2</val><val>3</val>" + "<val>5</val><val>9</val></fibonacci>";
NodeTest nodeTest = new NodeTest(testXML); assertNodeTestPasses(nodeTest, new FibonacciNodeTester(),
new short[] {Node.TEXT_NODE, Node.ELEMENT_NODE},
true);
}
private class FibonacciNodeTester extends AbstractNodeTester {
XMLUnit Java User’s Guide |
8 / 35 |
private int nextVal = 1, lastVal = 1, priorVal = 0;
public void testText(Text text) throws NodeTestException { int val = Integer.parseInt(text.getData());
if (nextVal != val) {
throw new NodeTestException("Incorrect value", text);
}
nextVal = val + lastVal; priorVal = lastVal; lastVal = val;
}
public void testElement(Element element) throws NodeTestException { String name = element.getLocalName();
if ("fibonacci".equals(name) || "val".equals(name)) { return;
}
throw new NodeTestException("Unexpected element", element);
}
public void noMoreNodes(NodeTest nodeTest) throws NodeTestException {
}
}
The test fails because the XML contains the wrong value for the last number in the sequence:
Expected node test to pass, but it failed! Incorrect value [#text: 9]
2 Using XMLUnit
2.1Requirements
XMLUnit requires a JAXP compliant XML parser virtually everywhere. Several features of XMLUnit also require a JAXP compliant XSLT transformer. If it is available, a JAXP compliant XPath engine will be used for XPath tests.
To build XMLUnit at least JAXP 1.2 is required, this is the version provided by the Java class library in JDK 1.4. The JAXP 1.3 (i.e. Java5 and above) XPath engine can only be built when JAXP 1.3 is available.
As long as you don’t require support for XML Namespaces or XML Schema, any JAXP 1.1 compliant implementations should work at runtime. For namespace and schema support you will need a parser that complies to JAXP 1.2 and supports the required feature. The XML parser shipping with JDK 1.4 (a version of Apache Crimson) for example is compliant to JAXP 1.2 but doesn’t support Schema validation.
XMLUnit is supposed to build and run on any Java version after 1.3 (at least no new hard JDK 1.4 dependencies have been added in XMLUnit 1.1), but it has only been tested on JDK 1.4.2 and above.
To build XMLUnit JUnit 3.x (only tested with JUnit 3.8.x) is required. It is not required at runtime unless you intend to use the
XMLTestCase or XMLAssert classes.
2.2Basic Usage
XMLUnit consists of a few classes all living in the org.custommonkey.xmlunit package. You can use these classes directly from your code, no matter whether you are writing a unit test or want to use XMLUnit’s features for any other purpose.
This section provides a few hints of where to start if you want to use a certain feature of XMLUnit, more details can be found in the more specific sections later in this document.
XMLUnit Java User’s Guide |
9 / 35 |
2.2.1Comparing Pieces of XML
Heart and soul of XMLUnit’s comparison engine is DifferenceEngine but most of the time you will use it indirectly via the Diff class.
You can influence the engine by providing (custom) implementations for various interfaces and by setting a couple of options on the XMLUnit class.
More information is available in Section 3.
2.2.2Validating
All validation happens in the Validator class. The default is to validate against a DTD, but XML Schema validation can be enabled by an option (see Validator.useXMLSchema).
Several options of the XMLUnit class affect validation.
More information is available in Section 4.
2.2.3XSLT Transformations
The Transform class provides an easy to use layer on top of JAXP’s transformations. An instance of this class is initialized with the source document and a stylesheet and the result of the transformation can be retrieved as a String or DOM Document.
The output of Transform can be used as input to comparisons, validations, XPath tests and so on. There is no detailed sections on transformations since they are really only a different way to create input for the rest of XMLUnit’s machinery. Examples can be found in Section 1.6.
It is possible to provide a custom javax.xml.transform.URIResolver via the XMLUnit.setURIResolver method.
You can access the underlying XSLT transformer via XMLUnit.getTransformerFactory.
2.2.4XPath Engine
The central piece of XMLUnit’s XPath support is the XpathEngine interface. Currently two implementations of the interface exist, SimpleXpathEngine and org.custommonkey.xmlunit.jaxp13.Jaxp13XpathEngine.
SimpleXpathEngine is a very basic implementation that uses your XSLT transformer under the covers. This also means it will expose you to the bugs found in your transformer like the transformer claiming a stylesheet couldn’t be compiled for very basic XPath expressions. This has been reported to be the case for JDK 1.5.
org.custommonkey.xmlunit.jaxp13.Jaxp13XpathEngine uses JAXP 1.3’s javax.xml.xpath package and seems to work more reliable, stable and performant than SimpleXpathEngine.
You use the XMLUnit.newXpathEngine method to obtain an instance of the XpathEngine. As of XMLUnit 1.1 this will try to use JAXP 1.3 if it is available and fall back to SimpleXpathEngine.
Instances of XpathEngine can return the results of XPath queries either as DOM NodeList or plain Strings.
More information is available in Section 5.
2.2.5DOM Tree Walking
To test pieces of XML by traversing the DOM tree you use the NodeTester class. Each DOM Node will be passed to a NodeTester implementation you provide. The AbstractNodeTester class is provided as a NullObject Pattern base class for implementations of your own.
More information is available in Section 6.