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).
Réponses
Trop de publicités?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.
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
Utilisez xmldiff , un outil python qui détermine les différences entre deux fichiers XML similaires, de la même manière que diff.
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.