Тестирование вглубь: моки и фикстуры
Фикстуры, параметризация, моки и покрытие: как тестировать код, который взаимодействует с реальным миром
assert над чистыми функциями. Реальные системы не чисты: они обращаются к сети, читают файлы, работают с базами данных и смотрят на часы. Чтобы протестировать такой код, нужно изолировать его от мира с помощью тестовых дублёров (моков), строить переиспользуемое состояние через фикстуры, прогонять один тест по многим входам с помощью параметризации, а затем измерять, что тесты на самом деле затрагивают, через покрытие. Эти четыре навыка превращают "я написал тест" в "я могу протестировать что угодно".
- писать функции
@pytest.fixtureи внедрять их по имени параметра, - выбирать правильную область видимости фикстуры и убирать за собой через
yield, - прогонять один тест по многим случаям с помощью
@pytest.mark.parametrize, - заменять реальные зависимости через
unittest.mockиpatch(), применяя правило "патчить там, где имя ищется", - настраивать возвращаемые значения, побочные эффекты и проверки на моке,
- измерять и читать покрытие и понимать, почему 100% не означает "без ошибок".
1. Фикстуры: настройка как внедрение зависимостей
Фикстура - это функция с декоратором @pytest.fixture, которая создаёт нужный тесту объект: временную базу данных, настроенный клиент, образец записи. Тест запрашивает фикстуру, просто указав её имя как параметр. pytest видит имя, выполняет фикстуру и передаёт её результат. Это внедрение зависимостей: тест объявляет, что ему нужно, а pytest это предоставляет.
import pytest
@pytest.fixture
def account():
return {"balance": 100, "owner": "ada"}
def test_withdraw(account): # pytest внедряет фикстуру по имени
account["balance"] -= 30
assert account["balance"] == 70
Обратите внимание, чего вы не написали: ни вызова настройки в начале теста, ни глобального состояния. Каждый тест, называющий account, получает свой свежесозданный словарь, поэтому тесты не протекают состоянием друг в друга. Фикстуры также могут запрашивать другие фикстуры, позволяя собирать из маленьких кусочков целое окружение.