With the addition of xml validation and value checking, the testing scripts actually become somewhat useful:
> PUT http://localhost:8080/ws/user ''
'username=testuser1&email_address=testuser1@test.com'
> GET http://localhost:8080/ws/user/testuser1
< assert status 200
< assert header Content-Type text/xml
< assert xmlschema ../web/user.xsd
< assert xmlbody /user/username=testuser1
< assert xmlbody /user/email_address=testuser1@test.com
> DELETE http://localhost:8080/ws/user/testuser1
Depak Vohra’s article at ONJava, shows how to perform schema validation using xerces/jaxp. Adapting his code to jython is straightforward, by adding the following imports to the test script from part 2:
from java.io import StringReader
from java.lang import System
from javax.xml.parsers import DocumentBuilderFactory
from javax.xml.parsers import DocumentBuilder
from org.xml.sax.helpers import DefaultHandler
from org.xml.sax import InputSource
System.setProperty('javax.xml.parsers.DocumentBuilderFactory',
'org.apache.xerces.jaxp.DocumentBuilderFactoryImpl')
Depak’s Validator becomes:
class Validator(DefaultHandler):
def __init__(self):
self.validationError = 0
self.saxParseException = None
def error(self, exception):
self.validationError = 1
self.saxParseException = exception
def fatalError(self, exception):
self.validationError = 1
self.saxParseException = exception
def warning(self, exception):
pass
Finally, a new assertion method is added to the Test class:
#
# assert that an xml document validates against an xml schema
#
def assert_xmlschema(self, linearr, req, res):
xml = res.getContent()
factory = DocumentBuilderFactory.newInstance()
factory.setNamespaceAware(1)
factory.setValidating(1)
factory.setAttribute('http://java.sun.com/xml/jaxp/properties/schemaLanguage',
'http://www.w3.org/2001/XMLSchema')
errhandler = Validator()
factory.setAttribute('http://java.sun.com/xml/jaxp/properties/schemaSource',
linearr[0])
builder = factory.newDocumentBuilder()
builder.setErrorHandler(errhandler)
dom = builder.parse(InputSource(StringReader(xml)))
if errhandler.validationError == 1:
errmsg = '%s, %s' % (str(errhandler.validationError),
errhandler.saxParseException.getMessage())
else:
errmsg = ''
assert errhandler.validationError != 1, errmsg
Value checking is accomplished with the following method:
#
# assert that a particular xml element contains a particular value
#
def assert_xmlbody(self, linearr, req, res):
xml = res.getContent()
if not self.xml or self.xml != xml:
factory = DocumentBuilderFactory.newInstance()
factory.setNamespaceAware(1)
builder = factory.newDocumentBuilder()
self.dom = builder.parse(InputSource(StringReader(xml)))
self.xml = xml
# get rid of the starting '/'
# (could leave this out in the test, but it looks better in to me)
if linearr[0].startswith('/'):
linearr[0] = linearr[0][1:]
# split into xml element list and a value based on the first '=' position
equalspos = linearr[0].find('=')
tmp = linearr[0][0:equalspos]
value = linearr[0][equalspos+1:]
elements = tmp.split('/')
# loop through the elements in the list and retrieve each named node
node = self.dom
for x in xrange(0, len(elements)):
nl = node.getElementsByTagName(elements[x])
if not nl:
raise AssertionError, 'sub element "%s" is missing' % elements[x]
else:
node = nl.item(0)
if x == len(elements)-1:
eval = node.getFirstChild().getNodeValue()
assert eval == value, 'element %s (value "%s") does not match expected %s'
% (elements[x], eval, value)
return
raise AssertionError, 'missing element %s' % tmp
Which takes an assertion line such as “assert xmlbody /user/username=testuser1″ and looks up elements in the dom, where “user” is the root node, and “username” is a child node of user. At the moment this is very basic value checking which won’t work well for more complicated documents — but I’m trying to avoid complication as much as possible anyway, so it’s difficult to justify (to myself at least) going to greater lengths to make something more flexible. The main enhancement I’m looking to make initially is the ability to specify the nth child of a node. For example, something like the string “/list/user[2]/username” (in case it’s not painfully obvious, the second “user” element in “list”).
But for the moment, this will hopefully get me going.


