Python 2.7 mock/patch: понимание assert_called_XYZ()

Я относительно новичок в Python и модульном тестировании на Python. Из мира Java я знаю концепцию насмешливости, но она, кажется, сильно отличается от того, что я вижу на Python.

Я нашел это руководство, которое мне очень помогло: http://www.voidspace.org.uk/python/mock/index.html

Но поскольку я написал мои (несколько более сложные) тесты с издеваемыми зависимостями, я заметил, что это поведение нечистоплотного. Я решил создать сокращенный простой пример, который также не работает, как я ожидаю.

Взгляните на это, результат и мое ожидание, которое я добавил в качестве комментариев:

import unittest
from mock import patch, Mock, MagicMock

class BasicTest(unittest.TestCase):

    @patch("StringIO.StringIO")
    def testSomethingNotWorkingAsExpected(self, StringIOMock):
        StringIOMock.assert_called_once() # asserts, but why?

    @patch("StringIO.StringIO")
    def testSomethingSomehowWorking(self, StringIOMock):
        # self.instantiateStringIO() # intentionally commented out
        assert StringIOMock.called # does not assert (leading to failure of this test); as expected. If the above line is not commented, this asserts as expected.

    def instantiateStringIO(self):
        import StringIO
        StringIO.StringIO()

Почему assert_called_once() утверждает создание экземпляра StringIO, даже если оно еще не было создано? И почему assert ClassMock.called приносит ожидаемые результаты?

Использование assert not ... для утверждения метода не было вызвано Я нашел здесь: Утвердить, что функция/метод не вызывается с использованием Mock. Я перевернул этот шаблон в моем случае, опуская not.

Где-то я нашел шаблон ClassMock.return_value для ссылки на экземпляр. Но я понимаю это как способ создания экземпляра Mock до того, как он будет вызван, а не как способ доступа к экземпляру, который может быть создан внутренним кодом. Или я ошибаюсь?

Моя среда:

  • Python 2.7.3
  • mock 0.8.8
  • Fedora 19

Возможно, мое понимание фальшивки/исправления неверно. Может ли кто-нибудь объяснить, что делает классный макет и как он работает?

Edit1: Добавлен вывод

... и добавил парафраз в parens для комментариев в testSomethingSomehowWorking

Это вывод:

.F
======================================================================
FAIL: testSomethingSomehowWorking (test_test.BasicTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/python2.7/site-packages/mock.py", line 1224, in patched
    return func(*args, **keywargs)
  File "test_test.py", line 15, in testSomethingSomehowWorking
    assert StringIOMock.called # does not assert; as expected
AssertionError

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

FAILED (failures=1)

Ответы

Ответ 1

Метод assert_called_once не существует и не выполняет утверждение. Это ничем не отличается от написания StringIOMock.assert_foo_bar_does_not_exist() или любого другого метода. Библиотека макета не проверяет, действительно ли существует метод, вызванный макетом.

Если вы используете assert_called_once_with, то он терпит неудачу, как ожидалось.

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

@patch("StringIO.StringIO", spec=StringIO.StringIO)
def testSomethingNotWorkingAsExpected(self, StringIOMock):
    StringIOMock.assert_called_once() # will fail as the method doesn't exist