Python 2 assertItemsEqual错误结果

时间:2019-02-01 18:37:08

标签: python python-unittest

在Python 2下使用unittest.TestCase.assertItemsEqual函数遇到了一个有趣的情况;在此张贴我的发现以供后代使用。

下面的单元测试应该在Python 2下成功完成:

import unittest

class Foo(object):
    def __init__(self, a=1, b=2):
        self.a = a
        self.b = b
    def __repr__(self):
        return '({},{})'.format(self.a, self.b)
    def __eq__(self, other):
        return self.a == other.a and self.b == other.b
    def __lt__(self, other):
        return (self.a, self.b) < (other.a, other.b)

class Test(unittest.TestCase):
    def test_foo_eq(self):
        self.assertEqual(sorted([Foo()]), sorted([Foo()]))
        self.assertItemsEqual([Foo()], [Foo()])

unittest.main()

以下是输出:

======================================================================
FAIL: test_foo_eq (__main__.Test)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/tsanders/scripts/one_offs/test_unittest_assert_items_equal2.py", line 17, in test_foo_eq
    self.assertItemsEqual([Foo()], [Foo()])
AssertionError: Element counts were not equal:
First has 1, Second has 0:  (1,2)
First has 0, Second has 1:  (1,2)

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (failures=1)

docs状态以来,这非常令人困惑:

  

[assertItemsEqualassertEqual(sorted(expected), sorted(actual))等效,但它也适用于不可哈希对象序列。

相同的测试在Python 3下通过(由于名称已更改,因此将self.assertItemsEqual交换为self.assertCountEqual之后。

编辑:发布此问题后,我确实找到了this个其他问题,涉及未定义__eq____hash__的情况。

1 个答案:

答案 0 :(得分:1)

要使测试在Python 2和3下都能通过,我必须将__hash__ = None行添加到Foo

assertItemsEqual / assertCountEqual函数采用不同的代码路径,具体取决于每个列表中的项目是否可哈希。并根据docs

  

如果一个类没有定义__cmp__()__eq__()方法,那么它也不应定义__hash__()操作;如果它定义了__cmp__()__eq__()而不是__hash__(),则其实例将无法在哈希集合中使用。

请记住,在定义__hash__时,Python 2和3在__eq__方面具有不同的行为:

  • 在Python 2中,定义__eq__不会影响默认提供的__hash__,但尝试在散列容器中使用该项会导致实现定义的行为(例如,键可能会重复,因为它们会重复即使__eq__返回True也具有不同的哈希值。
  • 在Python 3中,定义__eq__会将__hash__设置为None

从Python 2.6开始,__hash__可以显式设置为None,以使类不可散列。在我的案例中,要求使assertItemsEqual使用依赖于__eq__而不是__hash__的正确比较算法。