Вопрос проверяет понимание тестовых дублёров, умение управлять поведением моков и моделировать ошибки/последовательности вызовов.
side_effect задаёт “поведение” мока при вызове: он может выбрасывать исключение, возвращать разные значения по очереди или вызывать функцию. Это нужно, чтобы в тестах воспроизводить реальные сценарии: временные сбои, ретраи, разные ответы. Такой контроль делает тесты ближе к реальности и помогает покрывать негативные ветки.
side_effect — это механизм в unittest.mock (и совместимых инструментах), который определяет, что произойдёт при вызове мока: возврат значения, исключение, последовательность значений или выполнение функции.
side_effectПеред примерами важно разделить два понятия:
return_value — всегда возвращает одно и то же
side_effect — позволяет моделировать динамическое поведение
Это критично, когда код зависит от:
последовательности вызовов
временных ошибок
разных ответов внешнего сервиса
Например, внешний клиент падает, и вы проверяете обработку ошибки.
from unittest.mock import Mock
import pytest
def call_service(client):
return client.fetch()
def test_call_service_raises():
client = Mock()
client.fetch.side_effect = TimeoutError("timeout")
with pytest.raises(TimeoutError, match="timeout"):
call_service(client)
Полезно для ретраев: сначала ошибка, потом успех.
from unittest.mock import Mock
def get_with_retry(client):
try:
return client.fetch()
except TimeoutError:
return client.fetch()
def test_retry_success_after_failure():
client = Mock()
client.fetch.side_effect = [TimeoutError(), {"ok": True}]
assert get_with_retry(client) == {"ok": True}
assert client.fetch.call_count == 2
Это удобно, когда хотите сохранить логику вычисления, но контролировать окружение.
from unittest.mock import Mock
def test_side_effect_function():
def fake_fetch(x):
return {"id": x}
client = Mock()
client.fetch.side_effect = fake_fetch
assert client.fetch(10) == {"id": 10}
side_effectЕсли side_effect превращается в мини-сервис, тест становится тяжёлым. Лучше:
держать side_effect коротким
выносить сложное в helper-функцию
оставлять смысл теста очевидным
Если вы моделируете ретраи, важно фиксировать контракт:
assert client.fetch.call_count == 2
return_valueЕсли нужно всегда одно значение — используйте return_value, это проще и читабельнее.
return_value и side_effectПеред коротким списком важно обозначить правило выбора:
если поведение статичное → return_value
если поведение меняется или нужно исключение → side_effect
Одно значение всегда → return_value
Исключение → side_effect = SomeError(...)
Последовательность значений → side_effect = [.., ..]
Логика на аргументах → side_effect = function
side_effect — инструмент для моделирования “реальной жизни” в тестах: ошибок, последовательных ответов и вычисляемого поведения. Он делает тесты точнее, особенно для ретраев и обработки исключений, но его стоит держать простым и проверяемым.