-
Notifications
You must be signed in to change notification settings - Fork 12
Open
Labels
Description
It doesn't seem to be possible right now.
Possible workaround:
def assertXpathExists(self, node, xpath):
if self.eval_xpath(node, 'count(' + xpath + ')') == 0:
self.fail('''xpath "%s" doesn't exist''' % xpath)
def assertXpathNotExists(self, node, xpath):
if self.eval_xpath(node, 'count(' + xpath + ')') > 0:
self.fail('''xpath "%s" exists''' % xpath)
def eval_xpath(self, node, xpath):
expression = self.build_xpath_expression(node, xpath)
try:
return expression.evaluate(node)
except etree.XPathEvalError as error:
self.fail_xpath_error(node, expression.path, error)UPD I don't mean to offend you, but I ended up writing a bunch of my own helpers. The only thing I'm still using so far is assertXmlValidDTD(). They may be far from being perfect, but with them, my tests are much more readable. And they don't suffer from the flaw (the way I see it) that e.g. assertXpathValues() passes when the target element doesn't exist.
Leaving them here, for what it's worth:
from django.template import Template, RequestContext
from lxml import etree
import xml.dom.minidom as mdom
class XmlTestMixin:
# assertXML + can compare not the whole document but a subtree
# + can ignore subtrees
# _remove_doctype is a temporary fix
# assertXMLEqual bails out for documents containing doctype
# they have fixed it in the master
# https://code.djangoproject.com/ticket/30497
def assertXML(self, expected, actual, **kwargs):
if 'subtree' in kwargs:
actual = self._extract_subtree(actual, kwargs['subtree'])
if 'ignore' in kwargs:
actual = self._ignore_xpaths(actual, kwargs['ignore'])
self.assertXMLEqual(
self._remove_doctype(expected),
self._remove_doctype(actual))
def _extract_subtree(self, xml, xpath):
t = etree.fromstring(xml.encode()).getroottree()
return etree.tostring(
self.xpath(t, xpath)[0],
encoding=t.docinfo.encoding
).decode()
def _ignore_xpaths(self, xml, xpaths):
t = etree.fromstring(xml.encode()).getroottree()
for xp in xpaths:
for el in t.xpath(xp):
el.getparent().remove(el)
kwargs = {'encoding': t.docinfo.encoding, **(
{'xml_declaration': True}
if self._has_xml_declaration(xml)
else {}
)}
return etree.tostring(t, **kwargs).decode()
def _remove_doctype(self, xml):
dom = mdom.parseString(xml)
if not dom.version: # no xml declaration
return xml
for n in dom.childNodes:
if n.nodeType == mdom.Node.DOCUMENT_TYPE_NODE:
dom.removeChild(n)
return dom.toxml()
def _has_xml_declaration(self, xml):
return mdom.parseString(xml).version
# I usually start with a test that handles the general case
# (compare the whole document + ignore subtrees or compare a subtree)
# that's where Django templates come in handy
# succeeding tests deal with the details
def render_template(self, template, context, response):
return Template(template) \
.render(RequestContext(response.wsgi_request, context))
def assertElementExists(self, node, xpath):
if self.xpath(node, 'count(' + xpath + ')') == 0:
self.fail('''Element doesn't exist (%s)''' % xpath)
def assertElementNotExists(self, node, xpath):
if self.xpath(node, 'count(' + xpath + ')') > 0:
self.fail('Element exists (%s)' % xpath)
def assertElementText(self, root, xpath, text):
r = self.xpath(root, xpath)
self._assertOneElement(xpath, r)
if self._normalize_space(r[0].text) != text:
self.fail('''Element's text differs (%s)\n'''
'Expected: %s\n'
'Actual: %s\n'
% (xpath, text, r[0].text))
def assertElementsText(self, root, xpath, texts):
r = self.xpath(root, xpath)
if len(r) != len(texts):
self.fail('''Number of elements doesn't match'''
'that of the expected values (%s)'
% xpath)
actual_texts = set(self._normalize_space(e.text) for e in r)
if actual_texts != texts:
self.fail('''Elements' texts differ (%s)\n'''
'Expected: %s\n'
'Actual: %s\n'
% (xpath, texts, actual_texts))
def assertAttributeValue(self, root, xpath, attr, value):
r = self.xpath(root, xpath)
self._assertOneElement(xpath, r)
if attr not in r[0].attrib:
self.fail('''Attribute doesn't exist (%s)''' % xpath)
if r[0].attrib[attr] != value:
self.fail('Attribute value differs (%s)\n'
'Expected: %s\n'
'Actual: %s\n'
% (xpath, value, r[0].attrib[attr]))
def _assertOneElement(self, xpath, r):
if len(r) == 0:
self.fail('xpath "%s" not found' % xpath)
elif len(r) > 1:
self.fail('xpath "%s" resolves to multiple elements' % xpath)
def xpath(self, xml, xpath):
tree = etree.fromstring(xml.encode()).getroottree() \
if isinstance(xml, str) \
else xml
return tree.xpath(xpath)
def _normalize_space(self, s):
return " ".join(s.split())