41 votes

Comparaison de XML dans un test unitaire en Python

J'ai un objet qui peut se construire à partir d'une chaîne XML et s'écrire dans une chaîne XML. Je voudrais écrire un test unitaire pour tester le déclenchement rond à travers XML, mais j'ai du mal à comparer les deux versions XML. Les espaces et l'ordre des attributs semblent être les problèmes. Des suggestions sur la façon de procéder? Ceci est en Python, et j'utilise ElementTree (ce n'est pas vraiment ce qui importe ici car je traite uniquement du XML dans les chaînes à ce niveau).

17voto

Kozyarchuk Points 4613

Normalisez d'abord 2 XML, puis vous pouvez les comparer. J'ai utilisé ce qui suit en utilisant lxml

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

17voto

Mikhail Korobov Points 6225

C'est une vieille question, mais la réponse acceptée de Kozyarchuk ne fonctionne pas pour moi à cause de l'ordre des attributs, et la solution minidom ne fonctionne pas non plus (aucune idée pourquoi, je ne l'ai pas déboguée).

Voici ce que j'ai finalement trouvé:

 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)
 

Cela produit également un diff qui peut être utile en cas de gros fichiers xml.

7voto

bobince Points 270740

Si le problème est vraiment juste l'espace et de l'ordre des attributs, et vous n'avez pas d'autres constructions que du texte et des éléments à s'inquiéter, vous pouvez analyser les cordes à l'aide d'un analyseur XML et de comparer les nœuds manuellement. Voici un exemple d'utilisation minidom, mais on pourrait écrire la même chose dans le programme etree assez simplement:

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

Si vous avez besoin d'un plus approfondie de l'équivalence comparaison, les possibilités de d'autres types de noeuds dont CDATA, PIs, des références d'entité, les commentaires, les doctypes, espaces de noms et ainsi de suite, vous pouvez utiliser le DOM Niveau 3 de la méthode Noyau d'isEqualNode. Ni minidom ni programme etree cela, mais pxdom est une application qui prend en charge:

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

(Vous pouvez changer certains de la DOMConfiguration options sur l'analyse si vous avez besoin de spécifier si les références d'entité et des sections CDATA correspondent à leurs remplacé équivalents.)

Un peu plus rond-point de la façon de le faire serait d'analyser, puis de le ré-serialise à la forme canonique et de faire une comparaison de chaînes de caractères. Nouveau pxdom prend en charge les DOM Niveau 3 LS option " canonique-forme que vous pourriez utiliser pour le faire; une alternative à l'aide de la stdlib de minidom mise en œuvre consiste à utiliser c14n. Cependant, vous devez avoir le PyXML extensions de l'installation de sorte que vous ne pouvez toujours pas tout à fait le faire dans la 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

5voto

superjoe30 Points 6876

Utilisez xmldiff , un outil python qui détermine les différences entre deux fichiers XML similaires, de la même manière que diff.

3voto

Robert Rossney Points 43767

Pourquoi êtes-vous en examinant les données XML à tous?

Le moyen de tester la sérialisation d'un objet consiste à créer une instance de l'objet, de le sérialiser, désérialiser dans un nouvel objet, et de comparer les deux objets. Lorsque vous apportez une modification qui rompt la sérialisation ou de la désérialisation, ce test échouera.

La seule chose à vérifier les données XML va trouver pour vous, c'est si votre sérialiseur émettant un sur-ensemble de ce que l'deserializer l'exige, et le deserializer ignore silencieusement des trucs qu'il ne s'attend pas.

Bien sûr, si quelque chose d'autre va consommer les données sérialisées, c'est une autre affaire. Mais dans ce cas, vous devriez penser à l'établissement d'un schéma XML et de le valider.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X