Skip to content

Commit fa8b3de

Browse files
committed
Renaming assertXmlEquivalent to assertXmlEquivalentOutputs, and update documentation for clarity purpose. And up to 0.2.0.
1 parent 365d5f1 commit fa8b3de

5 files changed

Lines changed: 148 additions & 95 deletions

File tree

doc/conf.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,13 @@
4848
# built documents.
4949
#
5050
# The short X.Y version.
51-
version = '0.1'
51+
version = '0.2'
5252
# The full version, including alpha/beta/rc tags.
53-
release = '0.1-dev'
53+
release = '0.2.0'
5454

5555
# The language for content autogenerated by Sphinx. Refer to documentation
5656
# for a list of supported languages.
57-
#language = None
57+
language = 'en'
5858

5959
# There are two options for replacing |today|: either, you set today to some
6060
# non-false value, then it is used:

doc/index.rst

Lines changed: 75 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -413,64 +413,6 @@ XPath expression assertions
413413
# ...
414414

415415

416-
XML documents comparison assertion
417-
----------------------------------
418-
419-
.. py:method:: XmlTestMixin.assertXmlEquivalent(got, expect)
420-
421-
:param got: XML as text or Element node
422-
:param expect: XML as text
423-
424-
Asserts both XML are equivalent. The comparison ignores spaces within nodes
425-
and namespaces - if any - may be associated to diffrerent prefixes.
426-
427-
If a difference is found, a :class:`AssertionError` is raised. You can have
428-
the detailed mismatch in ``str(exc)``, ``exc`` being the raised exception.
429-
430-
.. rubric:: Example
431-
432-
::
433-
434-
# ...
435-
436-
def test_custom_test(self):
437-
# Same XML (with different spacings placements and attrs order)
438-
got = b"""<?xml version="1.0" encoding="UTF-8" ?>
439-
<root>
440-
<tag foo="bar" bar="foo">foo</tag>
441-
</root>"""
442-
got_root = self.assertXmlDocument(got)
443-
expected = b"""<?xml version="1.0" encoding="UTF-8" ?>
444-
<root><tag bar="foo" foo="bar"> foo </tag></root>"""
445-
446-
self.assertXmlEquivalent(got, expected)
447-
self.assertXmlEquivalent(got_root, expected)
448-
449-
# Same XML, but with different namespace prefixes
450-
got = b"""<?xml version="1.0" encoding="UTF-8" ?>
451-
<root xmlns:foo="mynamespace">
452-
<foo:tag>foo</foo:tag>
453-
</root>"""
454-
got_root = self.assertXmlDocument(got)
455-
expected = b"""<?xml version="1.0" encoding="UTF-8" ?>
456-
<root xmlns:bar="mynamespace">
457-
<bar:tag>foo</bar:tag>
458-
</root>"""
459-
self.assertXmlEquivalent(got, expected)
460-
self.assertXmlEquivalent(got_root, expected)
461-
462-
# Check comparison failure
463-
got = b"""<?xml version="1.0" encoding="UTF-8" ?>
464-
<root xmlns:foo="mynamespace">
465-
<foo:tag> difference here </foo:tag>
466-
</root>"""
467-
got_root = self.assertXmlDocument(got)
468-
with self.assertRaises(self.failureException):
469-
self.assertXmlEquivalent(got, expected)
470-
with self.assertRaises(self.failureException):
471-
self.assertXmlEquivalent(got_root, expected)
472-
473-
474416
XML schema conformance assertion
475417
--------------------------------
476418

@@ -648,3 +590,78 @@ your own schema objects in these various schema languages.
648590
"""
649591
root = test_case.assertXmlDocument(data)
650592
self.assertXmlValidRelaxNG(root, filename=relaxng_filename)
593+
594+
595+
XML documents comparison assertion
596+
----------------------------------
597+
598+
Sometimes, one may want to check a global XML document, because he know exactly
599+
what is expected, and can rely on a kind of "string compare". Of course, XML
600+
is not a simple string, and requires more than just an
601+
``assert data == expected``, because order of elements can vary, order of
602+
attributes, etc.
603+
604+
In these cases, one can use the powerful - also dangerous - feature of `LXML
605+
Output Checker`. See also the documentation of the module
606+
`doctestcompare <http://lxml.de/api/lxml.doctestcompare-module.html>`_ for
607+
more information on the underlying implementation.
608+
609+
And as always, remember that the whole purpose of this :py:mod:`xmlunittest`
610+
is to **not** compare XML formated string. But, whatever, this function could
611+
help. May be.
612+
613+
.. py:method:: XmlTestMixin.assertXmlEquivalentOutputs(data, expected)
614+
615+
:param string data: XML formated string to check
616+
:param string expected: XML formated string used as reference
617+
618+
Asserts both XML formated string are equivalent. The comparison ignores
619+
spaces within nodes and namespaces may be associated to diffrerent prefixes,
620+
thus requiring only the same URL.
621+
622+
If a difference is found, an :py:exc:`AssertionError` is raised, with the
623+
comparison failure's message as error's message.
624+
625+
.. note::
626+
627+
The name ``assertXmlEquivalentOutputs`` is cleary a way to prevent user
628+
to missunderstand the meaning of this assertion: it checks only similar
629+
**outputs**, not **document**.
630+
631+
.. note::
632+
633+
This method only accept ``string`` as arguments. This is an opinionated
634+
implementation choice, as the purpose of this method is to check
635+
the result outputs of an XML document.
636+
637+
638+
.. rubric:: Example
639+
640+
::
641+
642+
# ...
643+
644+
def test_custom_test(self):
645+
"""Same XML (with different spacings placements and attrs order)"""
646+
# This XML string should come from the code one want to test
647+
data = b"""<?xml version="1.0" encoding="UTF-8" ?>
648+
<root><tag bar="foo" foo="bar"> foo </tag></root>"""
649+
650+
# This is the former XML document one can expect, with pretty print
651+
expected = b"""<?xml version="1.0" encoding="UTF-8" ?>
652+
<root>
653+
<tag foo="bar" bar="foo">foo</tag>
654+
</root>"""
655+
656+
# This will pass
657+
test_case.assertXmlEquivalentOutputs(data, expected)
658+
659+
# This is another example of result, with a missing attribute
660+
data = b"""<?xml version="1.0" encoding="UTF-8" ?>
661+
<root>
662+
<tag foo="bar"> foo </tag>
663+
</root>
664+
"""
665+
666+
# This won't pass
667+
test_case.assertXmlEquivalentOutputs(data, expected)

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
]
2828

2929
setup(name='xmlunittest',
30-
version='0.1.1',
30+
version='0.2.0',
3131
description='Library using lxml and unittest for unit testing XML.',
3232
long_description=long_description,
3333
author='Florian Strzelecki',

test.py

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -813,46 +813,74 @@ def test_assertXmlValidRelaxNG_no_relaxng(self):
813813

814814
# -------------------------------------------------------------------------
815815

816-
def test_assertXmlEquivalent(self):
817-
"""Asserts assertXmlEquivalent raises when comparison failed.
816+
def test_assertXmlEquivalentOutputs(self):
817+
"""Asserts assertXmlEquivalentOutputs raises when comparison failed.
818+
819+
Basic assertion: same document, with different order of attributes,
820+
text with useless spaces, etc.
821+
818822
"""
819-
test_case = XmlTestCase(methodName='assertXmlEquivalent')
823+
test_case = XmlTestCase(methodName='assertXmlEquivalentOutputs')
820824

821825
# Same XML (with different spacings placements and attrs order)
822-
got = b"""<?xml version="1.0" encoding="UTF-8" ?>
826+
data = b"""<?xml version="1.0" encoding="UTF-8" ?>
823827
<root>
824828
<tag foo="bar" bar="foo">foo</tag>
825829
</root>"""
826-
got_root = test_case.assertXmlDocument(got)
827830
expected = b"""<?xml version="1.0" encoding="UTF-8" ?>
828831
<root><tag bar="foo" foo="bar"> foo </tag></root>"""
829832

830-
test_case.assertXmlEquivalent(got, expected)
831-
test_case.assertXmlEquivalent(got_root, expected)
833+
test_case.assertXmlEquivalentOutputs(data, expected)
834+
835+
# Not the right element given
836+
wrong_element = b"""<?xml version="1.0" encoding="UTF-8" ?>
837+
<root>
838+
<notTag foo="bar" bar="foo">foo</notTag>
839+
</root>"""
840+
841+
with self.assertRaises(test_case.failureException):
842+
test_case.assertXmlEquivalentOutputs(wrong_element, expected)
843+
844+
# Too many tag elements
845+
data = b"""<?xml version="1.0" encoding="UTF-8" ?>
846+
<root>
847+
<tag foo="bar" bar="foo">foo</tag>
848+
<tag foo="bar" bar="foo">foo</tag>
849+
</root>"""
850+
851+
with self.assertRaises(test_case.failureException):
852+
test_case.assertXmlEquivalentOutputs(wrong_element, expected)
853+
854+
def test_assertXmlEquivalentOutputs_namespaces(self):
855+
"""Asserts assertXmlEquivalentOutputs raises when comparison failed.
856+
857+
Assertion with different namespaces: the namespace URI is the same,
858+
but the prefix is different. In this case, the two XML are equivalents.
859+
860+
"""
861+
test_case = XmlTestCase(methodName='assertXmlEquivalentOutputs')
832862

833863
# Same XML, but with different namespace prefixes
834-
got = b"""<?xml version="1.0" encoding="UTF-8" ?>
864+
data = b"""<?xml version="1.0" encoding="UTF-8" ?>
835865
<root xmlns:foo="mynamespace">
836866
<foo:tag>foo</foo:tag>
837867
</root>"""
838-
got_root = test_case.assertXmlDocument(got)
868+
839869
expected = b"""<?xml version="1.0" encoding="UTF-8" ?>
840870
<root xmlns:bar="mynamespace">
841871
<bar:tag>foo</bar:tag>
842872
</root>"""
843-
test_case.assertXmlEquivalent(got, expected)
844-
test_case.assertXmlEquivalent(got_root, expected)
845873

846-
# Check comparison failure
847-
got = b"""<?xml version="1.0" encoding="UTF-8" ?>
848-
<root xmlns:foo="mynamespace">
849-
<foo:tag> difference here </foo:tag>
850-
</root>"""
851-
got_root = test_case.assertXmlDocument(got)
852-
with self.assertRaises(test_case.failureException):
853-
test_case.assertXmlEquivalent(got, expected)
874+
test_case.assertXmlEquivalentOutputs(data, expected)
875+
876+
wrong_namespace = b"""<?xml version="1.0" encoding="UTF-8" ?>
877+
<root xmlns:foo="not_the_same_namespace">
878+
<foo:tag>foo</foo:tag>
879+
</root>
880+
"""
881+
854882
with self.assertRaises(test_case.failureException):
855-
test_case.assertXmlEquivalent(got_root, expected)
883+
test_case.assertXmlEquivalentOutputs(wrong_namespace, expected)
856884

857885

858886
if __name__ == "__main__":

xmlunittest.py

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
from lxml.doctestcompare import LXMLOutputChecker, PARSE_XML
1111

1212

13+
__all__ = ['XmlTestMixin', 'XmlTestCase']
14+
15+
1316
class XmlTestMixin(object):
1417
"""Base mixin class for XML unittest.
1518
@@ -156,19 +159,6 @@ def assertXpathValues(self, node, xpath, values):
156159
% (node.tag, xpath, result,
157160
etree.tostring(node, pretty_print=True)))
158161

159-
def assertXmlEquivalent(self, got, expect):
160-
"""Asserts both xml parse to the same results
161-
`got` may be an XML string or lxml Element
162-
"""
163-
checker = LXMLOutputChecker()
164-
165-
if isinstance(got, etree._Element):
166-
got = etree.tostring(got)
167-
168-
if not checker.check_output(expect, got, PARSE_XML):
169-
message = checker.output_difference(doctest.Example("", expect), got, PARSE_XML)
170-
self.fail(message)
171-
172162
def assertXmlValidDTD(self, node, dtd=None, filename=None):
173163
"""Asserts XML node is valid according to the given DTD."""
174164
schema = None
@@ -226,6 +216,24 @@ def assertXmlValidRelaxNG(self, node, relaxng=None, filename=None):
226216
if not schema.validate(node):
227217
self.fail(schema.error_log.last_error)
228218

219+
def assertXmlEquivalentOutputs(self, data, expected):
220+
"""Asserts both XML outputs are equivalent.
221+
222+
This assertion use the powerful but dangerous feature of
223+
LXMLOutputChecker. Powerful because one can compare two XML document
224+
in their meaning, but dangerous because sometimes there is more to
225+
check than just a kind of output.
226+
227+
See LXMLOutputChecker documentation for more information.
228+
229+
"""
230+
checker = LXMLOutputChecker()
231+
232+
if not checker.check_output(expected, data, PARSE_XML):
233+
message = checker.output_difference(doctest.Example("", expected),
234+
data, PARSE_XML)
235+
self.fail(message)
236+
229237

230238
class XmlTestCase(unittest.TestCase, XmlTestMixin):
231239
"""XML test case for unit test using python unittest built-in package.

0 commit comments

Comments
 (0)