Мок-объекты используются для имитации ошибок и исключений, перехватывая вызовы зависимостей и заменяя их контролируемым поведением. Например:
from unittest.mock import patch, Mock
import pytest
def function_to_test(api_client):
try:
data = api_client.fetch_data()
return process_data(data)
except APIError as e:
return f"Error: {e}"
class APIError(Exception):
pass
def test_function_with_api_error():
mock_api_client = Mock()
mock_api_client.fetch_data.side_effect = APIError("Simulated API Error")
result = function_to_test(mock_api_client)
assert result == "Error: Simulated API Error"
Здесь side_effect
позволяет моку бросать исключение, имитируя сбой API. pytest
(или unittest
) используются для организации и запуска теста, а assert
подтверждает ожидаемое поведение.
Мок-объекты позволяют имитировать ошибки и исключения в тестах, чтобы проверить, как ваш код обрабатывает эти нештатные ситуации. Это критически важно для обеспечения надежности и отказоустойчивости. Вот несколько способов, как это сделать:
1. Использование side_effect
:
Свойство side_effect
мок-объекта может быть установлено в исключение, которое будет вызвано при каждом вызове мока.
import unittest
from unittest.mock import Mock
class MyClass:
def my_method(self, value):
# Предположим, что этот метод взаимодействует с внешним сервисом
pass
class TestMyClass(unittest.TestCase):
def test_my_method_raises_exception(self):
# Создаем мок для метода, который мы хотим проверить на обработку исключений
mock_method = Mock(side_effect=ValueError("Simulated error"))
# Создаем экземпляр класса, содержащего метод, который мы мокаем
instance = MyClass()
instance.my_method = mock_method
# Проверяем, что при вызове метода с моком возникает ожидаемое исключение
with self.assertRaises(ValueError) as context:
instance.my_method(10)
self.assertEqual(str(context.exception), "Simulated error")
В этом примере side_effect
устанавливается равным ValueError("Simulated error")
. Когда instance.my_method(10)
вызывается, вместо реального выполнения метода выбрасывается исключение ValueError
. assertRaises
проверяет, что именно это исключение и было выброшено, и что его сообщение соответствует ожидаемому.
2. Использование return_value
и условной логики в side_effect
:
Можно использовать side_effect
с функцией, которая решает, возвращать ли значение или вызывать исключение, основываясь на аргументах вызова.
import unittest
from unittest.mock import Mock
class MyClass:
def my_method(self, value):
# Предположим, что этот метод взаимодействует с внешним сервисом
pass
class TestMyClass(unittest.TestCase):
def test_my_method_conditional_exception(self):
def side_effect_func(value):
if value < 0:
raise ValueError("Value cannot be negative")
return value * 2
mock_method = Mock(side_effect=side_effect_func)
instance = MyClass()
instance.my_method = mock_method
# Проверяем исключение при отрицательном значении
with self.assertRaises(ValueError) as context:
instance.my_method(-5)
self.assertEqual(str(context.exception), "Value cannot be negative")
# Проверяем нормальное выполнение при положительном значении
result = instance.my_method(5)
self.assertEqual(result, 10)
Здесь side_effect
- это функция side_effect_func
, которая принимает аргумент value
. Если value
отрицательное, она выбрасывает ValueError
. В противном случае она возвращает value * 2
. Это позволяет имитировать различные сценарии ошибок в зависимости от входных данных.
3. Мокирование контекстных менеджеров, вызывающих исключения:
Можно мокировать контекстные менеджеры, чтобы они вызывали исключения при входе или выходе. Это полезно для тестирования операций, связанных с файлами или сетевыми соединениями.
import unittest
from unittest.mock import Mock, patch
class MyClass:
def read_file(self, filename):
with open(filename, 'r') as f:
return f.read()
class TestMyClass(unittest.TestCase):
@patch('__main__.open') # Обратите внимание: мокируем встроенную функцию open
def test_read_file_raises_exception(self, mock_open):
mock_file = Mock()
mock_file.read.side_effect = IOError("File not found") # Имитируем ошибку чтения файла
mock_open.return_value = mock_file # open() возвращает мок-объект, который имитирует файл
instance = MyClass()
with self.assertRaises(IOError) as context:
instance.read_file("nonexistent_file.txt")
self.assertEqual(str(context.exception), "File not found")
В этом примере мы мокируем встроенную функцию open
. mock_file.read.side_effect = IOError("File not found")
заставляет метод read()
мок-объекта "файла" выбрасывать исключение IOError
. Затем мы проверяем, что при вызове instance.read_file("nonexistent_file.txt")
выбрасывается ожидаемое исключение.
Важные замечания:
assertRaises
: Для проверки исключений используйте self.assertRaises(ExceptionType)
в unittest.