Указание «любого экземпляра класса Foo» для макета assert_called_once_with()?

#python #mocking

#python #издевательство

Вопрос:

В assert_called_once_with , как я могу указать параметр «любой экземпляр класса Foo»?

Например:

 class Foo(): pass
def f(x): pass
def g(): f(Foo())
import __main__
from unittest import mock
 

mock.ANY конечно, проходит:

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

и, конечно, другой экземпляр 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>)
 

Что я могу указать в качестве ожидаемого параметра, чтобы любой экземпляр Foo передал утверждение?

Комментарии:

1. Почему assert_called_once() тогда не assertIsInstance полученное значение?

2. Я думаю, это то, что я сделаю. Рад, что вы добавили это в свой ответ, оба решения являются информативными.

3. Мне очень нравится mock_f.assert_called_once_with(AnyInstanceOf(Foo)) , но YMMV!

4. Я попробую!

Ответ №1:

Одно простое решение — сделать это в два этапа:

 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)
 

Однако, если вы посмотрите на реализацию 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()
 

вы можете видеть, что это просто объект, который равен чему угодно. Таким образом, вы могли бы определить свой собственный эквивалент, равный любому экземпляру 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()
 

Или в более общем смысле:

 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)
 

В любом случае, вы можете использовать его по своему усмотрению ANY :

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