Python: Автоматическое тестирование
2026-02-26 23:16 Diff

Подготовка данных

Представим, что мы разрабатываем библиотеку pydash и хотим протестировать её функции для обработки коллекций:

  • includes()
  • size()
  • filter()
  • и другие (всего их около 20 штук)

Для работы этих функций нужна заранее подготовленная коллекция. Проще всего придумать одну, которая подойдёт для тестирования большинства или даже всех функций:

В примере выше видно, что в каждом тесте мы создаем одну и ту же коллекцию. А теперь представьте, что таких тестов несколько десятков - код начнёт кочевать из одного места в другое, порождая всё больше и больше копипасты.

Самый простой способ избежать этого — вынести определение коллекции на уровень модуля, вне тестовых функций:

Это простое решение убирает ненужное дублирование. Но у него есть серьезный недостаток. Коллекция теперь глобальная переменная, а значит любой тест, который изменяет ее, делает ее непригодной для последующих тестов.

Но даже если бы наш код не мутировал данные, все равно у такого подхода есть слабые стороны. Представьте себе такой код:

Подвох тут в том, что переменная now инициализируется один раз, во время загрузки модуля. Весь код, определённый на уровне модуля выполняется ровно один раз.

Почему это может быть проблемой? Код в тесте работает с понятием «сейчас» и рассчитывает на то, что «сейчас» это почти моментальный снимок данного момента времени. Но в примере выше, now начинает отставать от реального «сейчас» и чем больше тестов и чем они сложнее, тем большее отставание.

Для решения проблемы подготовки данных в pytest предлагается инструмент, который называется фикстуры. Ниже пример того, как создавать дату перед каждым тестом:

Чтобы создать фикстуру, нам нужно описать функцию, которая подготавливает наши данные и обернуть ее декоратором @pytest.fixture. А для использования, ее нужно передать в параметры теста.

Основное назначение фикстур - подготовка независимых данных. По умолчанию фикстура создается на каждый тест заново, что и гарантирует разделение тестов.

Тесты могут использовать сколько угодно фикстур. Да и сами фикстуры могут использовать другие фикстуры, точно также через передачу в параметры.

Помимо явной передачи в параметры теста можно задать "автоиспользование" фикстуры, указав параметр autouse=True. Такая фикстура будет автоматически использоваться всеми тестами без ее указания.

Автоиспользование фикстур применяется для подготовки внешних сервисов, тестовых серверов или баз данных.

Области видимости

Зачастую подготовка данных включает в себя более сложные действия, чем создание коллекции. Например, создать тестовый сервер, подключиться к базе данных и заполнить ее данными или создать пользователя через форму регистрации на сайте. Если производить эти операции перед каждым тестом, то это потребует больших затрат времени и памяти. Для решения этой проблемы у фикстур есть области видимости, scopes.

По умолчанию, у фикстур задана область видимости function, то есть фикстура создается на каждую функцию, которая ее использует, и уничтожается при ее выполнении. Но есть и другие области:

  • class - фикстура существует на весь класс с тестом
  • module - фикстура существует на весь модуль
  • package - фикстура существует на весь пакет с тестами
  • session - фикстура существует на всю тестовую сессию

Так, например, подготовка тестовой базы данных, которая нам нужна на протяжении всей тестовой сессии, может выглядеть как:

Совместные фикстуры

Помимо указания области видимости, pytest также позволяет создавать фикстуры, которые можно использовать во всех модулях. Для этого их нужно прописать в файле conftest.py. Все фикстуры, определенные в нем, могут быть использованы любым тестом в директории tests без необходимости их импортировать, pytest обнаружит их автоматически.

Часто это требуется для подготовки данных, которые потребуются на протяжении всего тестирования - пользовательские сессии, соединения с базой данных, запуск внешних "тяжелых" сервисов.

Очистка данных

В общем случае нам не нужно управлять очисткой данных, после завершения тестов, pytest самостоятельно удалит все фикстуры, очистив состояние после себя. Но иногда очистка может включать в себя освобождение каких-то ресурсов: закрытие файлов, возврат базы в изначальное состояние и прочие операции, обратные подготовке. Для этого в pytest существует выражение yield.

Основные различия, что в коде return заменяется на yield и любой код очистки ресурсов помещается после yield. После выполнения тестов pytest пройдется по фикстурам и выполнит очистку данных.

Помните, что мы все еще работаем с компьютером и не застрахованы от поломок. Ошибка в неподходящем месте может оставить нашу систему в нестабильном состоянии, которое отразится на последующих тестах. Создавая фикстуры, лучше ограничивать каждую лишь одним действием, изменяющим состояние. Например, если процесс регистрации пользователя состоит из этапов создания базы, записи пользователя в базу и отправки письма, то правильно будет разделить эти этапы на отдельные фикстуры со своей подготовкой и очисткой.

Встроенные фикстуры

В программировании часто приходится решать типовые задачи, уже не раз решенные раньше. Потому мы переиспользуем код, устанавливаем готовые библиотеки и применяем шаблоны. Pytest для частых задач предоставляет встроенные фикстуры. Среди них фикстуры для работы с временными директориями, кэшем или выводом в консоль. Чтобы их использовать, достаточно импортировать в тестах библиотеку pytest - встроенные фикстуры будут доступны сразу.

Выводы

Мы научились использовать инструмент подготовки данных - фикстуры. Узнали как создавать независимые данные для каждого теста, как использовать данные на протяжении разной длины тестирования и как их переиспользовать. Познакомились со встроенными фикстурами для типовых задач, а также узнали, как очистить результаты работы тестов и вернуть начальное состояние программы.