Frage

Ich habe ein Objekt, das sich aus einer XML-Zeichenfolge aufbauen können, und sich in eine XML-String schreiben. Ich möchte eine Unit-Test schreiben Runde Auslösung durch XML zu testen, aber ich habe Probleme beim Vergleich der beiden XML-Versionen. Whitespace und Attribut, um scheinen die Probleme zu sein. Irgendwelche Vorschläge, wie dies zu tun? Dies ist in Python, und ich bin mit ElementTree (nicht, dass das wirklich wichtig ist hier, da ich mit XML nur tun bin in Strings auf dieser Ebene).

War es hilfreich?

Lösung

Zuerst 2 XML normalisieren, dann kann man sie vergleichen. Ich habe die folgende using lxml verwendet

obj1 = objectify.fromstring(expect)
expect = etree.tostring(obj1)
obj2 = objectify.fromstring(xml)
result = etree.tostring(obj2)
self.assertEquals(expect, result)

Andere Tipps

Dies ist eine alte Frage, aber die akzeptierten Kozyarchuk Antwort nicht für mich bestellen, weil das Attribut nicht funktioniert, und die minidom Lösung nicht wie sie ist nicht funktioniert entweder (keine Ahnung warum, ich habe es nicht ausgetestet).

Das ist, was ich kam schließlich mit:

from doctest import Example
from lxml.doctestcompare import LXMLOutputChecker

class XmlTest(TestCase):
    def assertXmlEqual(self, got, want):
        checker = LXMLOutputChecker()
        if not checker.check_output(want, got, 0):
            message = checker.output_difference(Example("", want), got, 0)
            raise AssertionError(message)

Dies erzeugt auch einen Diff, die bei großen XML-Dateien hilfreich sein können.

Wenn das Problem wirklich nur das Leerzeichen ist und Attribut Ordnung, und Sie haben keine andere Konstrukte als Text und Elemente zu kümmern, können Sie die Saiten mit einem Standard-XML-Parser analysieren und die Knoten manuell vergleichen. Hier ist ein Beispiel minidom, aber Sie könnten das gleiche in etree schreiben ziemlich einfach:

def isEqualXML(a, b):
    da, db= minidom.parseString(a), minidom.parseString(b)
    return isEqualElement(da.documentElement, db.documentElement)

def isEqualElement(a, b):
    if a.tagName!=b.tagName:
        return False
    if sorted(a.attributes.items())!=sorted(b.attributes.items()):
        return False
    if len(a.childNodes)!=len(b.childNodes):
        return False
    for ac, bc in zip(a.childNodes, b.childNodes):
        if ac.nodeType!=bc.nodeType:
            return False
        if ac.nodeType==ac.TEXT_NODE and ac.data!=bc.data:
            return False
        if ac.nodeType==ac.ELEMENT_NODE and not isEqualElement(ac, bc):
            return False
    return True

Wenn Sie einen gründlichere Äquivalenzvergleich benötigen, für die Möglichkeiten anderer Typen von Knoten einschließlich CDATA, PIs, Entitätsverweise, Kommentare, doctypes, Namensräume und so weiter, könnten Sie die DOM Level 3 Core-Methode isEqualNode verwenden. Weder minidom noch etree hat das, aber pxdom ist eine Implementierung, die es unterstützt:

def isEqualXML(a, b):
    da, db= pxdom.parseString(a), pxdom.parseString(a)
    return da.isEqualNode(db)

(Sie können einige der DOMConfiguration Optionen auf der Parse ändern, wenn Sie müssen angeben, ob Entitätsverweise und CDATA-Abschnitte ihre Äquivalente ersetzt übereinstimmen.)

Ein etwas Umwege, es zu tun wäre, zu analysieren, dann wieder serialise auf kanonische Form und machen Sie einen String-Vergleich. Wieder pxdom unterstützt die DOM Level 3 LS Option ‚kanonische-Form‘, die Sie, dies zu tun verwenden könnten; eine alternative Art und Weise der stdlib der minidom Implementierung verwendet, ist C14N zu verwenden. Jedoch Sie die PyXML für diese so können Erweiterungen installieren müssen Sie noch nicht ganz tun es im stdlib:

from xml.dom.ext import c14n

def isEqualXML(a, b):
    da, bd= minidom.parseString(a), minidom.parseString(b)
    a, b= c14n.Canonicalize(da), c14n.Canonicalize(db)
    return a==b
Mit

xmldiff , ein Python-Tool, das die Unterschiede zwischen zwei ähnlichen XML-Dateien herausfindet, auf die gleiche Weise dass diff macht es.

Warum untersuchen Sie die XML-Daten überhaupt?

Die Art und Weise Objektserialisierung zu testen, ist eine Instanz des Objekts zu erstellen, serialisiert es, deserialisieren es in ein neues Objekt, und vergleichen Sie die beiden Objekte. Wenn Sie eine Änderung vornehmen, die Serialisierung oder Deserialisierung bricht, wird dieser Test nicht bestehen.

Das einzige, was die XML-Daten überprüft wird für Sie zu finden, wenn Ihr Serializer ist eine Obermenge zu emittieren, was der Deserializer erfordert, und der Deserializer ignoriert leise Sachen erwarten, dass es nicht.

Natürlich, wenn etwas anderes werden wird die serialisierten Daten raubend, das ist eine andere Sache. Aber in diesem Fall, sollten Sie darüber nachdenken, ein Schema für die XML-Festlegung und Validierung es.

Ich hatte auch dieses Problem und hat heute um es einige graben. Der doctestcompare Ansatz ausreichen, aber ich gefunden über Ian Bicking , dass es auf formencode.doctest_xml_compare basiert. Welche scheint nun zu sein, hier . Wie Sie sehen können, dass ist eine ziemlich einfache Funktion, im Gegensatz zu doctestcompare (obwohl ich denke, doctestcompare alle Ausfälle sammelt und vielleicht anspruchsvollere Kontrolle). Wie dem auch sei Kopieren / xml_compare Import aus formencode kann eine gute Lösung sein.

Die Java-Komponente dbUnit macht eine Menge XML-Vergleiche, so dass Sie könnte es nützlich sein bei ihrer Annäherung zu suchen (vor allem keine gotchas zu finden, die sie bereits angesprochen werden).

def xml_to_json(self, xml):
    """Receive 1 lxml etree object and return a json string"""
    def recursive_dict(element):
        return (element.tag.split('}')[1],
                dict(map(recursive_dict, element.getchildren()),
                     **element.attrib))
    return json.dumps(dict([recursive_dict(xml)]),
                      default=lambda x: str(x))

def assertEqualXML(self, xml_real, xml_expected):
    """Receive 2 objectify objects and show a diff assert if exists."""
    xml_expected_str = json.loads(self.xml_to_json(xml_expected))
    xml_real_str = json.loads(self.xml_to_json(xml_real))
    self.maxDiff = None
    self.assertEqual(xml_real_str, xml_expected_str)

Sie könnten einen Ausgang sehen, wie zum Beispiel:

                u'date': u'2016-11-22T19:55:02',
                u'item2': u'MX-INV0007',
         -      u'item3': u'Payments',
         ?                  ^^^
         +      u'item3': u'OAYments',
         ?                  ^^^ +

Es kann leicht mit minidom getan werden:

class XmlTest(TestCase):
    def assertXmlEqual(self, got, want):
        return self.assertEqual(parseString(got).toxml(), parseString(want).toxml())
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top