При использовании unittest.mock.patch, почему autospec не True по умолчанию?

Когда вы исправляете функцию с использованием макета, вы можете указать autospec как True:

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

(http://www.voidspace.org.uk/python/mock/patch.html)

Мне интересно, почему это не поведение по умолчанию? Разумеется, мы почти всегда хотели бы перехватывать некорректные параметры любой функции, которую мы исправляем?

Ответы

Ответ 1

Единственный ясный способ объяснить это, это на самом деле привести документацию в обратную сторону использования автосогласования и почему вы должны быть осторожны при использовании:

Это не без оговорок и ограничений, поэтому а не по умолчанию. Чтобы узнать, какие атрибуты доступный на объекте spec, autospec должен заглядывать (доступ атрибуты) spec. По мере прохождения атрибутов на макет соответствующий обход исходного объекта происходит под капот. Если какой-либо из ваших определенных объектов имеет свойства или дескрипторы которые могут вызывать выполнение кода, тогда вы не сможете использовать Autospec. С другой стороны, гораздо лучше спроектировать ваши объекты так что интроспекция безопасна [4].

Более серьезная проблема заключается в том, что для атрибутов, например, создаваться в init и не существовать в классе при все. autospec can not знает о любых динамически созданных атрибутах и ограничивает api видимыми атрибутами.

Я думаю, что ключевой вывод здесь - отметить эту строку: autospec can not знает о любых динамически созданных атрибутах и ​​ограничивает api видимыми атрибутами

Таким образом, чтобы более явное выражение примера прерывания автоспецификации, этот пример, взятый из документации, показывает следующее:

>>> class Something:
...   def __init__(self):
...     self.a = 33
...
>>> with patch('__main__.Something', autospec=True):
...   thing = Something()
...   thing.a
...
Traceback (most recent call last):
  ...
AttributeError: Mock object has no attribute 'a'

Как вы можете видеть, автоматическое определение не имеет представления о том, что при создании объекта Something создается атрибут a.

Как правило, для себя я просто издеваюсь над патчем и не использую autospec так много, поскольку поведение, как правило, соответствует моим ожиданиям.

Нет ничего плохого в присвоении значения атрибуту экземпляра.

Соблюдайте приведенный ниже функциональный пример:

import unittest
from mock import patch

def some_external_thing():
    pass

def something(x):
    return x

class MyRealClass:
    def __init__(self):
        self.a = some_external_thing()

    def test_thing(self):
        return something(self.a)



class MyTest(unittest.TestCase):
    def setUp(self):
        self.my_obj = MyRealClass()

    @patch('__main__.some_external_thing')    
    @patch('__main__.something')
    def test_my_things(self, mock_something, mock_some_external_thing):
        mock_some_external_thing.return_value = "there be dragons"
        self.my_obj.a = mock_some_external_thing.return_value
        self.my_obj.test_thing()

        mock_something.assert_called_once_with("there be dragons")


if __name__ == '__main__':
    unittest.main()

Итак, я просто говорю для своего тестового примера. Я хочу убедиться, что метод some_external_thing() не влияет на поведение моего unittest, поэтому я просто присваиваю атрибуту экземпляра макет за mock_some_external_thing.return_value = "there be dragons".