красивая печать assertEqual () для строк HTML - PullRequest
3 голосов
/ 04 ноября 2011

Я хочу сравнить две строки в тесте Python, которые содержат HTML.

Есть ли метод, который выводит результат в удобной для человека (разнохарактерной) версии?

Ответы [ 4 ]

2 голосов
/ 24 ноября 2011

Простой метод - убрать пробел из HTML и разделить его на список. Юнит-тест Python 2.7 (или бэкпорт 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 голосов
/ 04 ноября 2011

Я представил патч для этого несколько лет назад. Патч был отклонен, но вы все еще можете просмотреть его в списке ошибок python .

Я сомневаюсь, что вы захотите взломать файл 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 голос
/ 04 ноября 2011

Может быть, это довольно «многословное» решение.Вы можете добавить новую «функцию равенства» для вашего пользовательского типа (например: 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.

0 голосов
/ 10 ноября 2011

Я (тот, кто задает этот вопрос) сейчас использую 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))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...