Python: Автоматическое тестирование
Теория: Фикстуры
Подготовка данных
Представим, что мы разрабатываем библиотеку 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 - встроенные фикстуры будут доступны сразу.
Выводы
Мы научились использовать инструмент подготовки данных - фикстуры. Узнали как создавать независимые данные для каждого теста, как использовать данные на протяжении разной длины тестирования и как их переиспользовать. Познакомились со встроенными фикстурами для типовых задач, а также узнали, как очистить результаты работы тестов и вернуть начальное состояние программы.





