10.3 Usage

     

A basic XMLUnit test verifies that two XML strings are equivalent using the assert method assertXMLEqual( ) . The test class is derived from XMLTestCase , which also gives it access to the functionality of the base JUnit class TestCase . Example 10-1 illustrates such a test.

Example 10-1. Testing for equivalent XML content
 XMLElementTest.java import org.custommonkey.xmlunit.*; public class  XMLElementTest  extends  XMLTestCase  {    public void  testEmptyElement  ( ) throws Exception {       XMLElement element = new XMLElement("test");       String expected = "<test></test>";  assertXMLEqual  (expected, element.toString( ));    } } 

This example creates the test class XMLElementTest to test the class XMLElement . The test method testEmptyElement( ) creates an XMLElement named test and uses the assertXMLEqual( ) test assert method to verify its contents. Since the assert method may throw an Exception , the test method declaration also states that an Exception may be thrown. If the Exception is thrown, the unit test framework will catch the Exception and indicate that the test resulted in an error.

The tested class XMLElement is given in Example 10-2. It represents an XML element.

Example 10-2. The class XMLElement
 XMLElement.java public class  XMLElement  {    private String name;  XMLElement  (String n) {       name = n;    }    public String  toString  ( ) {       return "<"+name+"/>";    } } 

This initial version of XMLElement only knows how to represent an empty element. Its toString( ) method returns the XML string <test/> . Since a ssertXMLEquals( ) tests for syntactical equivalence, not literal string equivalence, comparing this string to the expected value <test></test> succeeds. Both strings are valid XML representations of an empty element named test.

A test can also verify that two XML strings are literally identical. All XMLUnit comparison tests use the class Diff to compare XML strings. The assertXMLEquals() method creates an instance of Diff and calls its method similar() to check if the two strings are equivalent. To test for identical XML strings, a Diff is created and passed to assertXMLIdentical( ) , as shown in Example 10-3.

Example 10-3. Testing for identical XML content
 XMLElementTest.java    public void  testEmptyElementIdentical  ( ) throws Exception {       XMLElement element = new XMLElement("test");       String expected = "<test/>";  Diff  diff = new Diff(expected, element.toString( ));  assertXMLIdentical  (diff, true);    } 

The second argument to assertXMLIdentical() , the Boolean TRUE , indicates that the test should pass if the XML strings are identical. Passing FALSE indicates that the test should pass if the strings are not identical.

An XML element can have both text content and child elements. Example 10-4 demonstrates unit tests for adding contents and children to XMLElement .

Example 10-4. Test adding content and children to XMLElement
 XMLElementTest.java    public void  testContent  ( ) throws Exception {       XMLElement element = new XMLElement("test", "content");       String expected = "<test>content</test>";  assertXMLEqual  (expected, element.toString( ));    }    public void  testAddChildren  ( ) throws Exception {       XMLElement element = new XMLElement("test");       XMLElement child = new XMLElement("child", "content");       XMLElement child2 = new XMLElement("child2", "content2");       element.  addChild  ( child );       element.  addChild  ( child2 );       String expected =          "<test><child>content</child><child2>content2</child2></test>";  assertXMLEqual  (expected, element.toString( ));    } 

Again, assertXMLEqual( ) is used to verify the XML produced by XMLElement . The new behaviors being tested are the generation of XML for an element with text content and for an element with two children, each with their own text content.

Example 10-5 implements XMLElement to support adding text content and children to an element.

Example 10-5. Version of XMLElement supporting text content and child elements
 XMLElement.java import java.util.*; public class  XMLElement  {    private String name;    private String content;    private Vector children;  XMLElement  (String n) {       name = n;       content = "";       children = new Vector( );    }  XMLElement  (String n, String c) {       name = n;       content = c;       children = new Vector( );    }    public void  addChild  (XMLElement child) {       children.addElement( child );    }    public String  toString  ( ) {       if ( content.length( ) == 0 && children.size( ) == 0 )          return "<"+name+"/>";       else {          String result = "<"+name+">"+content;          for (Enumeration e = children.elements( );               e.hasMoreElements( ); ) {             XMLElement element = (XMLElement)e.nextElement( );             result += element.toString( );          }          result += "</"+name+">";          return result;       }    } } 

A new XMLElement constructor allows content to be specified when an element is created. The method addChild( ) allows child elements to be added.

Testing individual XML elements is relatively easy. When it is necessary to unit test entire XML documents, XMLUnit really shows its usefulness . It supports building tests that compare and validate documents, as well as extracting and validating a document's nodes. With this test support, the task of building a custom XML document format becomes well-suited to test driven development methods . It makes sense to first test and build the functionality to write an empty document and then, as elements and attributes are added to the document format, to write additional tests for them.

The design of an XML document format is codified in a Document Type Definition (DTD). A DTD contains the specification for a particular document type in terms of both the elements it may contain and the contents of those elements. Elements may contain attributes, text contents, and child elements. An XML document may contain its own DTD as part of its header information, or it may contain a reference to another file where the DTD is found. It is a matter of personal choice whether you write unit tests that specifically test the contents of the DTD. Regardless of whether the DTD itself is tested, its validity is indirectly verified by unit tests that check the validity of XML documents that use it.

The most basic XMLUnit test to verify a document is assertXMLValid() . It takes an XML document represented as a string, parses it, and fails if there are any errors. Example 10-6 demonstrates validation of an XML document using this assertion.

Example 10-6. Test using assertXMLValid to validate an XML document
 LibraryXMLDocTest.java import org.custommonkey.xmlunit.*; public class  LibraryXMLDocTest  extends XMLTestCase {    public void  testValid  ( ) throws Exception {       Library library = new Library( );  LibraryXMLDoc  doc = new LibraryXMLDoc( library );  assertXMLValid  ( doc.toString( ) );    } } 

This test creates an instance of the new class LibraryXMLDoc and tests the validity of the XML document created by its toString( ) method. To pass the assertXMLValid( ) test, an XML document must contain a DOCTYPE definition, a DTD defining an element type, and a root element. Example 10-7 shows the simplest implementation of LibraryXMLDoc that will pass this test.

Example 10-7. Simple version of LibraryXMLDoc to produce a valid XML document
 LibraryXMLDoc.java public class  LibraryXMLDoc  {    public String  toString  ( ) {       return "<!DOCTYPE library ["          + "<!ELEMENT library (#PCDATA) >]>"          + "<library/>";    } } 

The XML document produced contains a DTD specifying the DOCTYPE library and an element type also named library . It contains one empty library element as well.

Since a Library contains Book s, the LibraryXMLDoc DTD logically specifies a root library element containing book elements. A book element has child title and author elements.

Example 10-8 shows the DTD for the library XML document implemented by LibraryXMLDoc .

Example 10-8. The library document DTD
 <!DOCTYPE library [    <!ELEMENT library (book*) >    <!ELEMENT book (title,author) >    <!ELEMENT title (#PCDATA) >    <!ELEMENT author (#PCDATA) > ]> 

This DTD specifies a document format in which a library element contains zero or more book elements, a book contains title and author elements, and the title and author elements contain character data.

Example 10-9 shows a unit test for an XML document that is compliant with this DTD. The document should contain a library element, which contains a book element. This in turn contains title and author elements. At this point, it also makes sense to refactor LibraryXMLDocTest into a fixture to reduce code duplication between tests.

Example 10-9. Testing the library, book, title, and author elements
 LibraryXMLDocTest.java import java.io.*; import org.w3c.dom.*; import javax.xml.parsers.*; import org.xml.sax.InputSource; import org.custommonkey.xmlunit.*; public class  LibraryXMLDocTest  extends XMLTestCase {    private Library library;    private  DocumentBuilder  builder;    public void  setUp  ( ) throws Exception {       library = new Library( );  DocumentBuilderFactory  builderFactory =          DocumentBuilderFactory.newInstance( );       builder = builderFactory.newDocumentBuilder( );    }    public void  testReadDocOneBook  ( ) throws Exception {       library.addBook(new Book("On the Road", "Jack Kerouac"));  LibraryXMLDoc  doc = new LibraryXMLDoc( library );  InputSource  in =          new InputSource(new StringReader( doc.toString( ) ));  Document  response = builder.parse( in );  NodeList  books = response.getElementsByTagName("book");       assertEquals(1, books.getLength( ));  Node  bookNode = books.item(0);  Node  titleNode = bookNode.getFirstChild( );  Text  titleText = (Text)titleNode.getFirstChild( );  Node  authorNode = bookNode.getLastChild( );  Text  authorText = (Text)authorNode.getFirstChild( );  assertEquals  ("On the Road", titleText.getData( ));  assertEquals  ("Jack Kerouac", authorText.getData( ));    } } 

The test method testReadDocOneBook( ) relies on several external objects to parse the XML document. These include a javax.xml.parsers.DocumentBuilder parser and Document , NodeList , Node , and Text objects to contain the parsed XML entities. Using these constructs, the test obtains the book , title , and author elements, and verifies the title and author text contents.

For the implementation of LibraryXMLDoc to pass these tests, see the end of this section.

The previous example parses a document and extracts individual node values. To avoid tedious repetitions of this kind of code, XMLUnit offers support for automatically walking the XML node tree with the NodeTest class and the NodeTester interface. Using this feature requires using an XML implementation that supports the DocumentTraversal interface, such as the Xerces parser.

Example 10-10 shows a unit test that uses NodeTest and NodeTester to walk the tree and check the element names and text content values.

Example 10-10. Walking the tree to test node values
 LibraryXMLDocTest.java    public void  testWalkTree  ( ) throws Exception {       XMLUnit.setControlParser(          "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl");       library.addBook(new Book("title1", "author1"));       library.addBook(new Book("title2", "author2"));  LibraryXMLDoc  doc3 = new LibraryXMLDoc( library );       String testDoc = doc3.toString( );  NodeTest  nodeTest = new NodeTest(testDoc);  assertNodeTestPasses  (nodeTest, new  LibraryNodeTester  ( ),          new short[] {Node.TEXT_NODE, Node.ELEMENT_NODE}, true);    }    private class  LibraryNodeTester  extends  AbstractNodeTester  {       private String currName = "";       public void  testText  (Text text) throws NodeTestException {          String txt = text.getData( );          System.out.println("text="+txt);          if ((currName.equals("title")                && txt.substring(0,5).equals("title"))            (currName.equals("author")                && txt.substring(0,6).equals("author")))             return;          throw new NodeTestException("Incorrect text value", text);       }       public void  testElement  (Element element)             throws NodeTestException {          String name = element.getLocalName( );          System.out.println("name="+name);          if (!name.equals("library") && !name.equals("book")             && !name.equals("title") && !name.equals("author"))             throw new NodeTestException("Unexpected name", element);          if (name.equals("title")  name.equals("author"))             currName = name;       }       public void  noMoreNodes  (NodeTest nodeTest)          throws NodeTestException {}    } 

The test method is named testWalkTree( ) . It first calls setControlParser( ) to use the Xerces parser. Next , it adds two Book s to the Library and creates a LibraryXMLDoc . An instance of NodeTest is created with the XML document string as its argument. The assert method assertNodeTestPasses() is called. This method takes an AbstractNodeTester as an argument, along with an array of Node types telling it which XML nodes to test.

The rest of the example is a custom AbstractNodeTester class named LibraryNodeTester that performs the actual testing of Node values. The method testElement( ) receives Element objects and the method testText( ) receives Text objects. In this example, the Elements are tested to verify they are either named library , book , title , or author , and the Text objects are tested to verify they contain the string title or author .

The following output from running the test demonstrates how it works. The NodeTest object traverses the document tree and passes all of the elements and text contents to LibraryNodeTester :

 $ java junit.textui.TestRunner LibraryXMLDocTest .name=library name=book name=title text=title2 name=author text=author2 name=book name=title text=title1 name=author text=author1 

A useful addition to the XML specification is the XML Path Language , known as XPath. It allows XML documents to be queried and manipulated using a URL-like path notation. XMLUnit offers a number of test assert methods that verify the results of XPath statements. Example 10-11 shows a unit test that creates XPath expressions to find book elements in a library XML document, and compares their results.

Example 10-11. Testing XPath expressions
 LibraryXMLDocTest.java    public void  testXpath( )  throws Exception {       library.addBook(new Book("On the Road", "Jack Kerouac"));       library.addBook(new Book("Dune", "Frank Herbert"));       LibraryXMLDoc doc = new LibraryXMLDoc( library );       String xmlTest = doc.toString( );  assertXpathExists  ("//book[title='Dune']", xmlTest);  assertXpathExists  ("//book[author='Jack Kerouac']", xmlTest);  assertXpathNotExists  ("//book[author='Nobody']", xmlTest);  assertXpathsEqual  ("//book[title='Dune']",          "//book[author='Frank Herbert']", xmlTest);  assertXpathsNotEqual  ("//book[title='Dune']",          "//book[title='On the Road']", xmlTest);    } 

This test creates XPath expressions to query book elements by title and author, and verifies that the books are found using assertXpathExists() . It also verifies whether two XPath expressions return the same element using the assert methods assertXpathsEqual( ) and assertXpathsNotEqual() .

For more details on XPath, see the official W3C XPath Recommendation at http://www.w3.org/TR/xpath.



Unit Test Frameworks
Unit Test Frameworks
ISBN: 0596006896
EAN: 2147483647
Year: 2006
Pages: 146
Authors: Paul Hamill

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net