pretty print assertEqual()用于HTML字符串

时间:2011-11-04 09:03:03

标签: python html unit-testing testing

我想比较python unittest中包含html的两个字符串。

是否有一种方法可以在人类友好(差异化)版本中输出结果?

4 个答案:

答案 0 :(得分:2)

几年前我提交了一个补丁来做这件事。修补程序已被拒绝,但您仍可以在python bug list上查看。

我怀疑你是不是想破解你的unittest.py来应用补丁(如果它在所有这些时间后仍然可以工作),但是这里的功能是将两个字符串减少到一个可管理的大小,同时仍保持至少部分不同的。只要您不想要完全不同,可能就是您想要的:

def shortdiff(x,y):
    '''shortdiff(x,y)

    Compare strings x and y and display differences.
    If the strings are too long, shorten them to fit
    in one line, while still keeping at least some difference.
    '''
    import difflib
    LINELEN = 79
    def limit(s):
        if len(s) > LINELEN:
            return s[:LINELEN-3] + '...'
        return s

    def firstdiff(s, t):
        span = 1000
        for pos in range(0, max(len(s), len(t)), span):
            if s[pos:pos+span] != t[pos:pos+span]:
                for index in range(pos, pos+span):
                    if s[index:index+1] != t[index:index+1]:
                        return index

    left = LINELEN/4
    index = firstdiff(x, y)
    if index > left + 7:
        x = x[:left] + '...' + x[index-4:index+LINELEN]
        y = y[:left] + '...' + y[index-4:index+LINELEN]
    else:
        x, y = x[:LINELEN+1], y[:LINELEN+1]
        left = 0

    cruncher = difflib.SequenceMatcher(None)
    xtags = ytags = ""
    cruncher.set_seqs(x, y)
    editchars = { 'replace': ('^', '^'),
                  'delete': ('-', ''),
                  'insert': ('', '+'),
                  'equal': (' ',' ') }
    for tag, xi1, xi2, yj1, yj2 in cruncher.get_opcodes():
        lx, ly = xi2 - xi1, yj2 - yj1
        edits = editchars[tag]
        xtags += edits[0] * lx
        ytags += edits[1] * ly

    # Include ellipsis in edits line.
    if left:
        xtags = xtags[:left] + '...' + xtags[left+3:]
        ytags = ytags[:left] + '...' + ytags[left+3:]

    diffs = [ x, xtags, y, ytags ]
    if max([len(s) for s in diffs]) < LINELEN:
        return '\n'.join(diffs)

    diffs = [ limit(s) for s in diffs ]
    return '\n'.join(diffs)

答案 1 :(得分:2)

一种简单的方法是从HTML中剥离空格并将其拆分为列表。 Python 2.7's unittest(或后向unittest2)然后在列表之间提供人类可读的差异。

import re

def split_html(html):
    return re.split(r'\s*\n\s*', html.strip())

def test_render_html():
    expected = ['<div>', '...', '</div>']
    got = split_html(render_html())
    self.assertEqual(expected, got)

如果我正在为工作代码编写测试,我通常首先设置expected = [],在断言之前插入self.maxDiff = None并让测试失败一次。然后可以从测试输出中复制粘贴预期的列表。

您可能需要根据HTML的外观调整如何删除空格。

答案 2 :(得分:1)

也许这是一个非常'冗长'的解决方案。您可以为您的用户定义类型添加新的“相等函数”(例如:HTMLString),您必须先定义它:

class HTMLString(str):
    pass

现在你必须定义一个类型相等函数:

def assertHTMLStringEqual(first, second):
    if first != second:
        message = ... # TODO here: format your message, e.g a diff
        raise AssertionError(message)

您所要做的就是根据需要格式化您的信息。您还可以在特定TestCase中使用类方法作为类型相等函数。这为您提供了更多格式化邮件的功能,因为unittest.TestCase会做很多事情。

现在您必须在unittest.TestCase

中注册此平等功能
...
def __init__(self):
    self.addTypeEqualityFunc(HTMLString, assertHTMLStringEqual)

类方法相同:

...
def __init__(self):
    self.addTypeEqualityFunc(HTMLString, 'assertHTMLStringEqual')

现在你可以在测试中使用它了:

def test_something(self):
    htmlstring1 = HTMLString(...)
    htmlstring2 = HTMLString(...)
    self.assertEqual(htmlstring1, htmlstring2)

这应该适用于python 2.7。

答案 3 :(得分:0)

我(问这个问题的人)现在使用BeautfulSoup:

def assertEqualHTML(string1, string2, file1='', file2=''):
    u'''
    Compare two unicode strings containing HTML.
    A human friendly diff goes to logging.error() if there
    are not equal, and an exception gets raised.
    '''
    from BeautifulSoup import BeautifulSoup as bs
    import difflib
    def short(mystr):
        max=20
        if len(mystr)>max:
            return mystr[:max]
        return mystr
    p=[]
    for mystr, file in [(string1, file1), (string2, file2)]:
        if not isinstance(mystr, unicode):
            raise Exception(u'string ist not unicode: %r %s' % (short(mystr), file))
        soup=bs(mystr)
        pretty=soup.prettify()
        p.append(pretty)
    if p[0]!=p[1]:
        for line in difflib.unified_diff(p[0].splitlines(), p[1].splitlines(), fromfile=file1, tofile=file2):
            logging.error(line)
        raise Exception('Not equal %s %s' % (file1, file2))