Я использую модуль mock
Python для выполнения своих тестов.
Бывают случаи, когда я издеваюсь над классом, однако я просто хочу издеваться над некоторыми его методами и свойствами, а не над всеми.
Предположим следующий сценарий:
# module.py
class SomeClass:
def some_method(self):
return 100
def another_method(self):
return 500
# test.py
class Tests(unittest.TestCase):
@patch('module.SomeClass')
def test_some_operation(self, some_class_mock):
some_class_instance = some_class_mock.return_value
# I'm mocking only the some_method method.
some_class_instance.some_method.return_value = 25
# This is ok, the specific method I mocked returns the value I wished.
self.assertEquals(
25,
SomeClass().some_method()
)
# However, another_method, which I didn't mock, returns a MagicMock instance
# instead of the original value 500
self.assertEquals(
500,
SomeClass().another_method()
)
В приведенном выше коде, как только я исправлю класс SomeClass
, вызовы методов, значения return_value которых я явно не установил, вернут MagicMock
объектов.
Мой вопрос: как я могу издеваться только над некоторыми методами класса, но при этом сохранять другие?
Есть два способа, которые я могу придумать, но ни один из них не является действительно хорошим.
Один из способов - установить метод макета в исходный метод класса, например:
some_class_instance.another_method = SomeClass.another_method
На самом деле это нежелательно, потому что у класса может быть много методов и свойств, которые нужно разблокировать.
Другой способ - явно исправить каждый метод, который я хочу, например:
@patch('module.SomeClass.some_method') def test_some_operation(self, some_method_mock):
Но на самом деле это не работает, если я хочу издеваться над самим классом, например, для издевательства над вызовами инициализатора. Приведенный ниже код в любом случае переопределит все методы
SomeClass
.@patch('module.SomeClass.some_method') @patch('module.SomeClass') def test_some_operation(self, some_class_mock, some_method_mock):
Вот более конкретный пример:
class Order:
def process_event(self, event, data):
if event == 'event_a':
return self.process_event_a(data)
elif event == 'event_b':
return self.process_event_b(data)
else:
return None
def process_event_a(self, data):
# do something with data
def process_event_b(self, data):
# do something different with data
В этом случае у меня есть общий метод process_event
, который вызывает конкретное событие обработки в зависимости от предоставленного события.
Я хотел бы протестировать только метод process_event
. Я просто хочу знать, вызывается ли правильное конкретное событие в зависимости от события, которое я предоставляю.
Итак, в моем тестовом примере я хочу смоделировать только process_event_a
и process_event_b
, вызвать исходный process_event
с определенными параметрами, а затем подтвердить, что process_event_a
или process_event_b
были вызваны с соответствующими параметрами.
SomeClass
. - person jonrsharpe   schedule 01.11.2020process_event
просто хочет, чтобы его событие было обработано; тот факт, что вы реализовали это как вызов различных методов, является деталями реализации. Проверив, что вы соединили свои тесты с текущей реализацией. - person jonrsharpe   schedule 05.11.2020