unittest unitarias skipif pruebas not integracion equal python xml elementtree

unitarias - Comparando XML en una prueba unitaria en Python



unittest python (8)

Tengo un objeto que puede compilarse a partir de una cadena XML y escribirse a sí mismo en una cadena XML. Me gustaría escribir una prueba unitaria para probar la ida y vuelta a través de XML, pero estoy teniendo problemas para comparar las dos versiones XML. El espacio en blanco y el orden de los atributos parecen ser los problemas. ¿Alguna sugerencia de cómo hacer esto? Esto está en Python, y estoy usando ElementTree (no es lo que realmente importa aquí ya que estoy tratando con XML en cadenas en este nivel).


¿Por qué estás examinando los datos XML en absoluto?

La forma de probar la serialización de objetos es crear una instancia del objeto, serializarlo, deserializarlo en un nuevo objeto y comparar los dos objetos. Cuando realice un cambio que interrumpe la serialización o la deserialización, esta prueba fallará.

Lo único que comprobará si los datos XML van a encontrar es si su serializador está emitiendo un superconjunto de lo que requiere el deserializador, y el deserializador ignora silenciosamente las cosas que no espera.

Por supuesto, si algo más va a consumir los datos serializados, ese es otro asunto. Pero en ese caso, debería estar pensando en establecer un esquema para el XML y validarlo.


Si el problema es solo el espacio en blanco y el orden de los atributos, y no tiene más construcciones que el texto y los elementos de los que preocuparse, puede analizar las cadenas mediante un analizador XML estándar y comparar los nodos manualmente. Aquí hay un ejemplo usando minidom, pero podrías escribir lo mismo en etree de forma bastante simple:

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 necesita una comparación de equivalencia más completa, que cubra las posibilidades de otros tipos de nodos, incluidos CDATA, PI, referencias de entidades, comentarios, doctypes, espacios de nombres, etc., podría utilizar el método DOM Level 3 Core isEqualNode. Ni minidom ni etree tienen eso, pero pxdom es una implementación que lo soporta:

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

(Es posible que desee cambiar algunas de las opciones de configuración de DOM en el análisis si necesita especificar si las referencias de entidad y las secciones de CDATA coinciden con sus equivalentes reemplazados).

Una forma un poco más indirecta de hacerlo sería analizar, luego volver a serializar a la forma canónica y hacer una comparación de cadenas. De nuevo, pxdom admite la opción LS de DOM Level 3 ''canonical-form'' que puede usar para hacer esto; una forma alternativa de usar la implementación minidom de stdlib es usar c14n. Sin embargo, debe tener las extensiones PyXML instaladas para esto, por lo que aún no puede hacerlo dentro de 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


Use xmldiff , una herramienta de python que descifra las diferencias entre dos archivos XML similares, de la misma manera que diff lo hace.


El componente Java dbUnit realiza una gran cantidad de comparaciones XML, por lo que puede resultar útil analizar su enfoque (especialmente para encontrar cualquier problema que ya hayan abordado).


También tuve este problema y lo exploré hoy. El método doctestcompare puede ser suficiente, pero a través de Ian Bicking descubrí que está basado en formencode.doctest_xml_compare . Lo cual parece estar ahora aquí . Como puede ver, es una función bastante simple, a diferencia de doctestcompare (aunque supongo que doctestcompare recopila todos los errores y tal vez una comprobación más sofisticada). De todos modos, copiar / importar xml_compare de formencode puede ser una buena solución.


Esta es una vieja pregunta, pero la respuesta aceptada por Kozyarchuk no funciona para mí debido al orden de los atributos, y la solución minidom tampoco funciona como está (ni idea por qué, no la he depurado).

Esto es lo que finalmente se me ocurrió:

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)

Esto también produce una diferencia que puede ser útil en el caso de archivos xml grandes.


Primero normaliza 2 XML, luego puedes compararlos. He usado lo siguiente usando lxml

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


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)

Podrías ver un resultado como:

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