using XMLUnit in python, or alternatives

Viewed 99

I'm pretty new to Python, but spent many years using XMLUnit to compare XML files for my unit tests.

I'd like to do something equally simple in python, but XMLUnit doesnt seem to be ported to Python?

There is something called XMLDiff, but its quite complex, and using my simple rig of identical files, reports hundreds of whitespace differences...

Ideally there would be a port of XmlUnit? or something very similar...worst case, I write an app in java that takes two files and then use that to compare the files and save the answer in a text file....horrid.

How do people do nice simple XML unit tests?

1 Answers

Depending on how granular you want the equality-test to be, this is fairly simple:

from pathlib import Path
from xml.etree.ElementTree import Element, parse
from io import StringIO


test1 = r"""<?xml version="1.0"?>
<lol>stuff
    <kek>lolkek</kek>keklol
</lol>
"""

test2 = r"""<?xml version="1.0"?>
<lol>different
    <kek>lolkek</kek>keklol
</lol>
"""


def compare_xml_files(file1: Path, file2: Path) -> bool:
    if not file1.is_file() or not file2.is_file():
        raise FileNotFoundError()
    with file1.open("r") as f1:
        with file2.open("r") as f2:
            return compare_xml_elements(parse(f1.read()).getroot(), parse(f2.read()).getroot())


def compare_xml_strings(xml1: str, xml2: str) -> bool:
    return compare_xml_elements(parse(StringIO(xml1)).getroot(), parse(StringIO(xml2)).getroot())


def compare_xml_elements(elem1: Element, elem2: Element) -> bool:
    if elem1.tag != elem2.tag:
        return False
    if elem1.text != elem2.text:
        return False
    if elem1.tail != elem2.tail:
        return False
    attr1 = elem1.attrib
    attr2 = elem2.attrib
    if len(attr1) != len(attr2):
        return False

    attr1keys = sorted(attr1.keys())
    attr2keys = sorted(attr2.keys())

    for attr1, attr2 in zip(attr1keys, attr2keys):
        if attr1 != attr2:
            return False

    # This will fail for Equal Children in different orders
    for child1, child2 in zip(iter(elem1), iter(elem2)):
        if not compare_xml_elements(child1, child2):
            return False

    return True


if __name__ == "__main__":
    print(compare_xml_strings(test1, test1))
    print(compare_xml_strings(test2, test2))
    print(compare_xml_strings(test1, test2))

This is more of a proof of concept, if you want more granular Error-Reporting, replace all return False with raise <your Error here>. Tested with Python 3.8.

Related