PyCharm 当多行字符串的单元测试失败时显示完整差异?

PyCharm show full diff when unittest fails for multiline string?

我正在使用 "unittest" 框架和 运行 在 PyCharm 中编写一些 Python 单元测试。一些测试将生成的长字符串与从文件中读取的参考值进行比较。如果此比较失败,我想使用 PyCharms 差异查看器查看两个比较字符串的差异。

所以代码是这样的:

    actual = open("actual.csv").read()
    expected = pkg_resources.resource_string('my_package', 'expected.csv').decode('utf8')
    self.assertMultiLineEqual(actual, expected)

和 PyCharm 很好地将测试标识为失败并在结果中提供 link window 单击以打开差异查看器。但是,由于 unittest 如何缩短结果,我在差异查看器中得到如下结果:

左侧:

'时间[57 个字符]ercent 0;1;1;1;1;1;1;1 0;2;1;3;4;2;3;1 0;3;[110 个字符]32 '

右侧:

'时间[57 个字符]ercen 0;1;1;1;1;1;1;1 0;2;1;3;4;2;3;1 0;3;2[109 个字符]32 '

现在,我想去掉所有 [X 个字符] 部分,只查看整个文件和 PyCharm 完全可视化的实际差异。

我试图查看单元测试代码,但找不到打印完整结果的配置选项。有一些变量,例如 maxDiff 和 _diffThreshold 但它们对此打印没有影响。

此外,我在 py.test 中尝试 运行 这个,但是 PyCharm 中的支持甚至更少(甚至没有 link 测试失败)。

是否有将 difflib 与 unittest 结合使用的一些技巧,或者使用另一个 Python 测试框架的其他技巧来做到这一点?

好吧,出于测试目的,我设法解决了这个问题。我没有使用 unittest 中的 assertEqual 方法,而是编写了自己的方法并在 unittest 测试用例中使用它。失败时,它会给我完整的文本,PyCharm diff 查看器也会正确显示完整的 diff。

我的断言语句在它自己的模块中 (t_assert.py),看起来像这样

def equal(expected, actual):
    msg = "'"+actual+"' != '"+expected+"'"
    assert expected == actual, msg

在我的测试中,我会这样称呼它

    def test_example(self):
        actual = open("actual.csv").read()
        expected = pkg_resources.resource_string('my_package', 'expected.csv').decode('utf8')
        t_assert.equal(expected, actual)
        #self.assertEqual(expected, actual)

到目前为止似乎有效..

许多地方给出的TestCase.maxDiff=None答案仅确保单元测试输出中显示的差异是全长的。为了在 <点击查看差异> link 中获得完整差异,您必须设置 MAX_LENGTH.

import unittest

# Show full diff in unittest
unittest.util._MAX_LENGTH=2000

来源:

这里的一个相关问题是 unittest.TestCase.assertMultiLineEqual 是用 difflib.ndiff() 实现的。这会产生非常大的差异,其中包含所有共享内容以及差异。如果你使用 difflib.unified_diff() 来代替 monkey patch,你会得到更小的差异,并且很少被截断。这通常避免了设置 maxDiff 的需要。

import unittest
from unittest.case import _common_shorten_repr
import difflib


def assertMultiLineEqual(self, first, second, msg=None):
    """Assert that two multi-line strings are equal."""
    self.assertIsInstance(first, str, 'First argument is not a string')
    self.assertIsInstance(second, str, 'Second argument is not a string')

    if first != second:
        firstlines = first.splitlines(keepends=True)
        secondlines = second.splitlines(keepends=True)
        if len(firstlines) == 1 and first.strip('\r\n') == first:
            firstlines = [first + '\n']
            secondlines = [second + '\n']
        standardMsg = '%s != %s' % _common_shorten_repr(first, second)
        diff = '\n' + ''.join(difflib.unified_diff(firstlines, secondlines))
        standardMsg = self._truncateMessage(standardMsg, diff)
        self.fail(self._formatMessage(msg, standardMsg))

unittest.TestCase.assertMultiLineEqual = assertMultiLineEqual