【问题标题】:How to perform assert_has_calls for a __getitem__() call?如何为 __getitem__() 调用执行 assert_has_calls?
【发布时间】:2019-08-24 09:37:15
【问题描述】:

代码:

from unittest.mock import MagicMock, call

mm = MagicMock()
mm().foo()['bar']

print(mm.mock_calls)
print()

mm.assert_has_calls([call(), call().foo(), call().foo().__getitem__('bar')])

输出:

[call(), call().foo(), call().foo().__getitem__('bar')]

Traceback (most recent call last):
  File "foo.py", line 9, in <module>
    mm.assert_has_calls([call(), call().foo(), call().foo().__getitem__('bar')])
TypeError: tuple indices must be integers or slices, not str

如何修复这个断言?

【问题讨论】:

    标签: python mocking python-3.7 python-unittest assertion


    【解决方案1】:

    这是一个错误,因为您应该始终能够使用 call 对象的 repr 输出来重新创建具有相同值的新 call 对象。

    这里的问题是callunittest.mock._Call 的一个实例,依赖于__getattr__ 方法来实现它的链式调用注解魔法,当一个不存在的属性名是返回另一个_Call 对象时给定的。但是由于_Calltuple 的子类,它确实定义了__getitem__ 属性,所以当要求__getitem__ 属性时,_Call.__getattr__ 方法将简单地返回tuple.__getitem__ 而不是_Call 对象。由于tuple.__getitem__ 不接受字符串作为参数,因此您会收到上述错误。

    为了解决这个问题,由于确定是否定义了属性是通过调用__getattribute__ 方法完成的,当找不到给定的属性名称时会引发AttributeError,我们可以覆盖_Call.__getattribute__这样当给定的属性名称为'__getitem__' 时,它会引发这样的异常,以有效地使__getitem__“不存在”并将其解析传递给__getattr__ 方法,然后该方法将返回一个_Call 对象就像它对任何其他不存在的属性一样:

    def __getattribute__(self, attr):
        if attr == '__getitem__':
            raise AttributeError
        return tuple.__getattribute__(self, attr)
    
    call.__class__.__getattribute__ = __getattribute__ # call.__class__ is _Call
    

    这样:

    mm = MagicMock()
    mm().foo()['bar']
    mm.assert_has_calls([call(), call().foo(), call().foo().__getitem__('bar')])
    

    不会引发异常,而:

    mm.assert_has_calls([call(), call().foo(), call().foo().__getitem__('foo')])
    

    会提高:

    AssertionError: Calls not found.
    Expected: [call(), call().foo(), call().foo().__getitem__('foo')]
    Actual: [call(), call().foo(), call().foo().__getitem__('bar')]
    

    演示:https://repl.it/repls/StrikingRedundantAngle

    请注意,我已在 Python 错误跟踪器中提交了 bug 并将我的修复作为 pull request 提交给 CPython,因此希望您在不久的将来不再需要执行上述操作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-12-11
      • 2020-05-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多