为模拟assert_used_once_with()指定“类Foo的任何实例”?

Mar*_*son 6 python mocking

在 中assert_called_once_with,如何指定参数是“Foo 类的任何实例”?

例如:

class Foo(): pass
def f(x): pass
def g(): f(Foo())
import __main__
from unittest import mock
Run Code Online (Sandbox Code Playgroud)

mock.ANY当然通过:

with mock.patch.object(__main__, 'f') as mock_f:
  g()
  mock_f.assert_called_once_with(mock.ANY)
Run Code Online (Sandbox Code Playgroud)

当然,Foo 的另一个实例不会通过。

with mock.patch.object(__main__, 'f') as mock_f:
  g()
  mock_f.assert_called_once_with(Foo())

AssertionError: Expected call: f(<__main__.Foo object at 0x7fd38411d0b8>)
                  Actual call: f(<__main__.Foo object at 0x7fd384111f98>)
Run Code Online (Sandbox Code Playgroud)

我可以将什么作为我的预期参数,以便 Foo 的任何实例都将使断言通过?

jon*_*rpe 5

一个简单的解决方案是分两步完成此操作:

with mock.patch.object(__main__, 'f') as mock_f:
    g()
    mock_f.assert_called_once()
    self.assertIsInstance(mock_f.mock_calls[0].args[0], Foo)
Run Code Online (Sandbox Code Playgroud)

但是,如果你看看以下的实现ANY

class _ANY(object):
    "A helper object that compares equal to everything."

    def __eq__(self, other):
        return True

    def __ne__(self, other):
        return False

    def __repr__(self):
        return '<ANY>'

ANY = _ANY()
Run Code Online (Sandbox Code Playgroud)

你可以看到它只是一个等于任何东西的对象。因此,您可以定义自己的等效项,它等于 的任何实例Foo

class AnyFoo:
    "A helper object that compares equal to every instance of Foo."

    def __eq__(self, other):
        return isinstance(other, Foo)

    def __ne__(self, other):
        return not isinstance(other, Foo)

    def __repr__(self):
        return '<ANY Foo>'


ANY_FOO = AnyFoo()
Run Code Online (Sandbox Code Playgroud)

或者更一般地说:

class AnyInstanceOf:
    "A helper object that compares equal to every instance of the specified class."

    def __init__(self, cls):
        self.cls = cls

    def __eq__(self, other):
        return isinstance(other, self.cls)

    def __ne__(self, other):
        return not isinstance(other, self.cls)

    def __repr__(self):
        return f"<ANY {self.cls.__name__}>"


ANY_FOO = AnyInstanceOf(Foo)
Run Code Online (Sandbox Code Playgroud)

无论哪种方式,您都可以像您想的那样使用它ANY

with mock.patch.object(__main__, 'f') as mock_f:
    g()
    mock_f.assert_called_once_with(ANY_FOO)
Run Code Online (Sandbox Code Playgroud)