Python单元测试中多行字符串的比较

8
当我在Python单元测试中比较两个Unicode字符串时,它会给出一个很好的失败信息,突出显示不同的行和字符。然而,比较两个8位字符串只显示两个字符串,没有突出显示。
如何才能让Unicode和8位字符串都有突出显示?
下面是一个示例单元测试,展示了这两种比较:
import unittest

class TestAssertEqual(unittest.TestCase):
    def testString(self):
        a = 'xax\nzzz'
        b = 'xbx\nzzz'
        self.assertEqual(a, b)

    def testUnicode(self):
        a = u'xax\nzzz'
        b = u'xbx\nzzz'
        self.assertEqual(a, b)

if __name__ == '__main__':
    unittest.main()

这个测试的结果显示了差异:
FF
======================================================================
FAIL: testString (__main__.TestAssertEqual)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/mnt/data/don/workspace/scratch/scratch.py", line 7, in testString
    self.assertEqual(a, b)
AssertionError: 'xax\nzzz' != 'xbx\nzzz'

======================================================================
FAIL: testUnicode (__main__.TestAssertEqual)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/mnt/data/don/workspace/scratch/scratch.py", line 12, in testUnicode
    self.assertEqual(a, b)
AssertionError: u'xax\nzzz' != u'xbx\nzzz'
- xax
?  ^
+ xbx
?  ^
  zzz

----------------------------------------------------------------------
Ran 2 tests in 0.001s

FAILED (failures=2)

Python 3更新

在Python 3中,默认情况下,字符串字面量是Unicode编码的,因此这基本上是无关紧要的。assertMultiLineEqual()不再支持字节字符串,所以你只能使用常规的assertEqual(),除非你愿意将字节字符串解码为Unicode。

1个回答

9
一番对Python源代码的深入研究表明,TestCase 注册了一系列用于测试不同类型相等性的方法。
self.addTypeEqualityFunc(dict, 'assertDictEqual')
self.addTypeEqualityFunc(list, 'assertListEqual')
self.addTypeEqualityFunc(tuple, 'assertTupleEqual')
self.addTypeEqualityFunc(set, 'assertSetEqual')
self.addTypeEqualityFunc(frozenset, 'assertSetEqual')
try:
    self.addTypeEqualityFunc(unicode, 'assertMultiLineEqual')
except NameError:
    # No unicode support in this build
    pass

你可以看到,unicode被注册使用assertMultiLineEqual(),但是str没有被注册为任何特殊用途。我不知道为什么str被排除在外,但迄今为止,我对下面两种方法都很满意。

直接调用

如果一个8位字符串默认情况下没有被注册使用assertMultiLineEqual(),你仍然可以直接调用它。

def testString(self):
    a = 'xax\nzzz'
    b = 'xbx\nzzz'
    self.assertMultiLineEqual(a, b)

注册字符串类型

您也可以自己注册它。只需在测试用例的setUp()方法中添加一行额外的代码。只需要执行一次,所有测试方法都将使用正确的方法来测试相等性。如果您的项目有一个适用于所有测试用例的公共基类,那么将其放置在那里是一个很好的选择。

class TestAssertEqual(unittest.TestCase):
    def setUp(self):
        super(TestAssertEqual, self).setUp()
        self.addTypeEqualityFunc(str, self.assertMultiLineEqual)

    def testString(self):
        a = 'xax\nzzz'
        b = 'xbx\nzzz'
        self.assertEqual(a, b)

    def testUnicode(self):
        a = u'xax\nzzz'
        b = u'xbx\nzzz'
        self.assertEqual(a, b)

任何一种方法都会在字符串比较失败时进行高亮显示。

注册字符串类型对我不起作用,但直接调用方法可以。 - 2rs2ts
1
简述:对于所有情况都使用 assertMultiLineEqual。 - Pierre.Sassoulas
哇,这也太复杂了吧。有没有类似 pytest 的变体可以用来解决这个问题? - Zephaniah Grunschlag
1
除非你被迫使用Python 2,否则@ZephaniahGrunschlag,这不应该再是一个问题了。在Python 3中,字符串字面量默认为Unicode。 - Don Kirkby
感谢@DonKirkby。我的问题是我在比较多行的f-string和标准的多行字符串。它会给其中一个添加大量的\n字符,而另一个则不会。然后我将标准字符串转换为f-string,比较就可以工作了(但我必须忽略使用没有插入变量的f-string的lint警告)。 - Zephaniah Grunschlag
1
听起来你应该问一个单独的问题,@ZephaniahGrunschlag。据我所知,F-strings不应该改变空格行为。 - Don Kirkby

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接