#python #unit-testing #mocking #python-unittest
#питон #модульное тестирование #осмеяние #python-unittest
Вопрос:
У меня есть вопрос, связанный с Python unittest.mock.Mock
и spec_set
функциональными возможностями.
Моя цель-создать макет со следующими функциями:
- У него есть спецификация произвольного класса, который я решаю во время создания.
- Я должен иметь возможность назначать на макет только атрибуты или методы в соответствии со спецификацией пункта 1
- Макет должен вызывать ошибку атрибута в следующих ситуациях:
- Я пытаюсь присвоить атрибут, которого нет в спецификации
- Я вызываю или извлекаю свойство , которое либо отсутствует в
spec_set
, либо присутствует вspec_set
, но назначено в соответствии с вышеуказанным пунктом.
Некоторые примеры поведения, которые я хотел бы:
class MyClass: property: int = 5 def func() -gt; int: pass # MySpecialMock is the Mock with the functionalities I am dreaming about :D mock = MyMySpecialMock(spec_set=MyClass) mock.not_existing # Raise AttributeError mock.func() # Raise AttributeError mock.func = lambda: "it works" mock.func() # Returns "it works"
Я перепробовал несколько решений, но безуспешно или не будучи явно многословным. Ниже приведены некоторые примеры:
- Использование
Mock(spec_set=...)
, но это не вызывает ошибок в случае, если я вызываю определенный атрибут, который я явно не устанавливал - Использование
Mock(spec_set=...)
и явное переопределение каждого атрибута функцией с побочным эффектом исключения, но это довольно многословно, так как я должен повторить все атрибуты…
Моя цель-найти способ автоматизировать 2, но у меня нет чистого способа сделать это. Сталкивались ли вы когда-нибудь с такой проблемой и решали ли ее?
Для любопытных цель состоит в том, чтобы улучшить разделение модульных тестов; Я хочу быть уверенным, что мои насмешки вызываются только в тех методах, которые я явно задал, чтобы избежать странных и неожиданных побочных эффектов.
Заранее благодарю вас!
Ответ №1:
spec_set
определяет mock
объект, который совпадает с классом, но затем не позволяет вносить в него какие-либо изменения, поскольку он определяет специальные __getattr__
и __setattr__
. Это означает, что первый тест (вызов несуществующего attr) завершится неудачей, как и ожидалось, но затем будет предпринята попытка установить attr:
from unitest import mock class X: pass m = mock.Mock(spec_set=X) m.func() # __getattr__: AttributeError: Mock object has no attribute 'func' m.func = lambda: "it works" # __setattr__: AttributeError: Mock object has no attribute 'func'
Вместо этого вы можете использовать create_autospec()
, который копирует существующую функцию и добавляет к mock
ней функции, но не влияет на __setattr__
:
n = mock.create_autospec(X) n.func() # __getattr__: AttributeError: Mock object has no attribute 'func' n.func = lambda: "it works" n.func() # 'it works'
Комментарии:
1. Спасибо вам за ответ! Боюсь, это будет создавать различное поведение, чем то, что я описал; моя цель в том, чтобы у этого сорта
X
, как: « класс X: деф кнопку func(): пройти « со следующим поведением: « mock_x.как func() # функцией getattr, : AttributeError: макет объект не имеет атрибута ‘Функ-Н.функции = лямбда: «это работает» Н.как func() # ‘работает’ « я к тому, чтоfunc
, действительно, должно быть собственностью спецификаций, ноgetattr
должны работать только после того, какsetattr
был призван.
Ответ №2:
Я думаю, что нашел удовлетворительный ответ на свою проблему, используя этот dir
метод.
Чтобы создать макет с требованиями, которые я перечислил выше, должно быть достаточно выполнить следующее:
def create_mock(spec: Any) -gt; Mock: mock = Mock(spec_set=spec) attributes_to_override = dir(spec) for attr in filter(lambda name: not name.startswith("__"), attributes_to_override): setattr(mock, attr, Mock(side_effect=AttributeError(f"{attr} not implemented"))) return mock