Ответ 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"
.