Как устроены фикстуры в pytest и как вы подходите к мокам и параметризации?
Короткий ответ
- Фикстура — функция с @pytest.fixture, внедряется по имени аргумента
- Scope управляет временем жизни: function, module, session
- yield в фикстуре даёт setup и teardown в одном месте
- conftest.py делает фикстуры доступными без импорта
- parametrize запускает тест на наборе данных
- Мокать лучше границы системы: сеть, время, БД
Фикстуры дают декларативную подготовку и очистку окружения с управлением временем жизни, а параметризация и точечные моки делают тесты короткими и честными.
Как сказать вслух
пример ответаФикстура в pytest — это функция, которая готовит данные или окружение, а тест получает её просто по имени аргумента. Через yield в ней же описывается очистка, а scope управляет, создаётся она на каждый тест или один раз на сессию — так делают, например, подключение к базе. Параметризация позволяет прогнать один тест на таблице случаев. Мокаю я в основном внешние границы — HTTP, время, очереди, а не внутренности своего кода, иначе тесты проверяют моки, а не логику.
Подробный ответ
Основной ответ
pytest внедряет фикстуры по именам параметров теста: функция с @pytest.fixture вызывается автоматически, её результат попадает в тест. Фикстуры могут зависеть друг от друга и образуют граф. Код после yield выполняется как teardown даже при падении теста. Scope (function по умолчанию, class, module, session) контролирует переиспользование: дорогие ресурсы — контейнер с БД, клиент приложения — поднимают один раз на сессию. conftest.py публикует фикстуры на каталог без импортов. @pytest.mark.parametrize генерирует отдельный тест на каждый набор аргументов. Для моков используют monkeypatch или unittest.mock/pytest-mock; правило — подменять границы (HTTP-клиенты, часы, файловую систему), а наружный контракт проверять интеграционными тестами.
Ключевые моменты
- Внедрение по имени. Тест объявляет нужное окружение аргументами — зависимости явные, дублирования setUp нет.
- Scope и стоимость. session-фикстуры экономят минуты на дорогой инициализации, но требуют осторожности с общим состоянием.
- Параметризация. Таблица «вход — ожидание» в одном декораторе; каждый случай отображается отдельным тестом в отчёте.
- Дисциплина моков. Мокаем то, чем не управляем (сеть, время, сторонние API); перемоканный тест перестаёт ловить реальные баги.
Практический контекст
В реальных проектах встречаются фикстуры клиента FastAPI/Django, тестовой БД в транзакции с откатом, фабрики данных (factory_boy). Интервьюер спрашивает про scope и teardown, просит спроектировать тест для функции с HTTP-вызовом — и смотрит, что именно кандидат замокает. Красный флаг для него — моки внутренних функций собственного модуля.
Пример кода
import pytest
@pytest.fixture
def db():
conn = create_test_db()
yield conn
conn.close() # teardown выполнится даже при падении теста
@pytest.mark.parametrize("raw,expected", [
(" Hi ", "hi"),
("ABC", "abc"),
("", ""),
])
def test_normalize(raw, expected):
assert normalize(raw) == expectedЧастые ошибки
- Делают изменяемую session-фикстуру и получают зависящие друг от друга тесты
- Мокают внутренние функции своего кода вместо внешних границ
- Патчат объект не в том модуле: нужно патчить там, где имя используется, а не где определено