unittest.mock
используйте patch
, patch.object
, patch.multiple
и подобные функции в блоке with
. Контекстный менеджер автоматически выполняет start()
при входе в блок with
и stop()
при выходе, гарантируя, что моки будут активны только в пределах этого блока и восстановлены после его завершения.
Контекстные менеджеры в `unittest.mock` предоставляют удобный способ автоматической настройки и восстановления мок-объектов в пределах определенного блока кода. Это особенно полезно для написания более чистого и читаемого кода, а также для предотвращения ошибок, связанных с забытым восстановлением исходных объектов.
Основные контекстные менеджеры, предоставляемые `unittest.mock`, это `patch`, `patch.object`, `patch.multiple`, и `patch.dict`. Каждый из них позволяет временно заменить объект, атрибут объекта, несколько атрибутов объекта или словарь соответственно, мок-объектом на время выполнения блока `with`. После выхода из блока `with`, оригинальное состояние восстанавливается автоматически.
Примеры:
import unittest
from unittest.mock import patch, Mock
import os
class MyClass:
def my_method(self):
return os.path.exists('/tmp/my_file')
class TestMyClass(unittest.TestCase):
def test_my_method_with_patch(self):
my_object = MyClass()
# Используем patch как контекстный менеджер для замены os.path.exists
with patch('os.path.exists', return_value=True) as mock_exists:
result = my_object.my_method()
self.assertTrue(result)
mock_exists.assert_called_once_with('/tmp/my_file')
# После выхода из блока with, os.path.exists восстановлен в исходное состояние
def test_my_method_with_patch_object(self):
my_object = MyClass()
# Используем patch.object как контекстный менеджер для замены метода my_method
with patch.object(my_object, 'my_method', return_value=True) as mock_method:
result = my_object.my_method()
self.assertTrue(result)
mock_method.assert_called_once()
# После выхода из блока with, my_object.my_method восстановлен
def test_patch_multiple(self):
with patch.multiple(os.path, exists=Mock(return_value=True), isdir=Mock(return_value=False)) as mocks:
exists_mock = mocks['exists']
isdir_mock = mocks['isdir']
self.assertTrue(os.path.exists('/tmp/something'))
self.assertFalse(os.path.isdir('/tmp/something'))
exists_mock.assert_called_once()
isdir_mock.assert_called_once()
def test_patch_dict(self):
my_dict = {'a': 1, 'b': 2}
with patch.dict(my_dict, {'a': 3, 'c': 4}):
self.assertEqual(my_dict['a'], 3)
self.assertEqual(my_dict['b'], 2)
self.assertEqual(my_dict['c'], 4)
self.assertEqual(my_dict['a'], 1)
self.assertEqual(my_dict['b'], 2)
self.assertNotIn('c', my_dict)
if __name__ == '__main__':
unittest.main()
Объяснение:
Использование контекстных менеджеров делает тесты более чистыми, лаконичными и менее подверженными ошибкам при настройке и очистке мок-объектов.