用Python比较单元测试中的XML

2024-04-25 22:40:23 发布

您现在位置:Python中文网/ 问答频道 /正文


Tags: python
3条回答

如果问题实际上只是空白和属性顺序,并且除了文本和元素之外没有其他构造要担心,那么可以使用标准XML解析器解析字符串并手动比较节点。下面是一个使用minidom的示例,但是您可以在etree中简单地编写相同的代码:

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

如果需要更彻底的等价性比较,包括其他类型节点(包括CDATA、PIs、实体引用、注释、doctype、名称空间等)的可能性,可以使用DOM级别3的核心方法isEqualNode。minidom和etree都没有,但pxdom是一个支持它的实现:

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

(如果需要指定实体引用和CDATA节是否与其替换的等价项匹配,则可能需要更改解析中的某些DOMConfiguration选项。)

一种更为迂回的方法是解析,然后重新序列化为规范形式并进行字符串比较。同样,pxdom支持DOM级别3 LS选项“canonical form”,您可以使用该选项;使用stdlib的minidom实现的另一种方法是使用c14n

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

首先规范化2个XML,然后可以比较它们。我使用了lxml

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

这是一个老问题,但是接受的Kozyarchuk's answer由于属性顺序而对我不起作用,minidom solution也不起作用(不知道为什么,我还没有调试它)。

这就是我最后想到的:

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)

这也会产生一个diff,在处理大型xml文件时会很有帮助。

相关问题 更多 >