Как запретить исправление обезьяны несуществующими методами макетов?

Я хотел бы написать тест, который поможет мне определить, не изменился ли API библиотеки, которую я использую, например. после обновления.

Если бы я создал объект «слепой макет», тогда макет всегда будет использовать один метод, и тесты будут проходить, но мое приложение сломается с фактической библиотекой.

Я знаю, что есть способ исправить существующие объекты:

@patch.object(ZipFile, 'namelist')
def test_my_method(self, mocked_zipfile):

который, по крайней мере, проверит, действительно ли метод namelist существует в исходном объекте, но все же позволяет мне сделать опечатку при насмешке над объектом внутри:

@patch.object(ZipFile, 'namelist')
def test_my_method(self, mocked_zipfile):
    mocked_zipfile.namlist.return_value = [ 'one.txt', 'two.txt' ]

Когда я делаю опечатку (namlist) внутри теста и внутри тестируемого кода, тест просто молча проходит.

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


person Radek Simko    schedule 25.04.2014    source источник
comment
Зачем вообще нужен мокап? Если у вас есть опечатка, тест провалится — разве это не желаемое поведение?   -  person alecxe    schedule 28.04.2014
comment
@alecxe Это зависит от того, потерпит ли он неудачу, он вызовет исходный метод без макета, который может потерпеть неудачу, а может и нет. Мне нужен макет, потому что я создаю модульные тесты, а не интеграционные или любые другие тесты. Я хочу протестировать функцию, изолированную от других функций и ресурсов.   -  person Radek Simko    schedule 29.04.2014


Ответы (2)


Вы можете исправить zipfile.Zipfile с помощью autospec=True:

Если вы установите autospec=True, то макет будет создан со спецификацией заменяемого объекта. Все атрибуты макета также будут иметь спецификацию соответствующего атрибута заменяемого объекта. Методы и функции, над которыми издевались, будут проверять свои аргументы и будут вызывать TypeError, если они вызываются с неправильной подписью. Для моков, заменяющих класс, их возвращаемое значение («экземпляр») будет иметь ту же спецификацию, что и класс.

Следующий тест не будет выполнен из-за AttributeError: Mock object has no attribute 'namlist':

from unittest import TestCase
from mock import patch

class MyTestCase(TestCase):
    @patch.object(ZipFile, 'namelist', autospec=True)
    def test_my_method(self, mocked_zipfile):
        mocked_zipfile.namlist.return_value = [ 'one.txt', 'two.txt' ]

Надеюсь, это поможет.

person alecxe    schedule 29.04.2014

Вы пытались использовать аргумент ключевого слова wraps?

Это работает для меня:

>>> from mock import Mock
>>> import zipfile
>>> mocked_zipfile = Mock(wraps=zipfile.ZipFile)
>>> mocked_zipfile.namlist.return_value = ['one.txt', 'two.txt']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/jbaiter/.envs/spreads/local/lib/python2.7/site-packages/mock.py", line 670, in __getattr__
    wraps = getattr(self._mock_wraps, name)
AttributeError: type object 'ZipFile' has no attribute 'namlist'
>>> mocked_zipfile.namelist.return_value = ['one.txt', 'two.txt']
>>> mocked_zipfile.namelist()
['one.txt', 'two.txt']

Я еще не пробовал использовать декорированный метод @patch, но это должно сработать:

@patch('zipfile.ZipFile', Mock(wraps=zipfile.ZipFile))
def test_my_method(self, mocked_zipfile):
    # call code that depends on ZipFile
    pass
person jbaiter    schedule 29.04.2014