我想比较python unittest中包含html的两个字符串。
是否有一种方法可以在人类友好(差异化)版本中输出结果?
答案 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))