Python: Продвинутое тестирование
Теория: Тестирование кода, взаимодействующего с файлами
Самый типичный побочный эффект – это взаимодействие с файлами. В основном это либо чтение файлов, либо запись в них. В этом уроке вы узнаете, как справиться с такими побочными эффектами при тестировании.
С чтением разбираться значительно проще, поэтому с него и начнем.
Чтение файлов
В большинстве случаев чтение файлов не доставляет особых проблем. Оно ничего не изменяет и выполняется локально, в отличие от сетевых запросов. Это значит, что при наличии необходимого файла и нужных прав, вероятность случайных ошибок крайне низка.
При тестировании функций, читающих файлы, должно выполняться ровно одно условие — функция должна позволять менять путь до файла. В таком случае достаточно создать файл нужной структуры в фикстурах:
Но в тестах читать /etc/passwd нельзя, потому что содержимое этого файла зависит от системного окружения, в котором запущены тесты. Потому для тестирования нужно создать файл аналогичной структуры в фикстурах и указать его при запуске функции:
Запись файлов
С записью файлов уже сложнее. Главная проблема — это отсутствие гарантированной идемпотентности. Это значит, что повторный вызов функции, записывающей файлы, может вести себя не как первый вызов. Например, он может завершаться с ошибкой или приводить к другим результатам.
Разберемся, почему так происходит. Представьте, что мы пишем тесты на функцию log(message), которая дописывает все переданные в нее сообщения в файл:
Это значит, что каждый запуск тестов будет немного другим. При первом запуске тестов создается файл для хранения логов. Затем он начнет заполняться. Это приводит к целой пачке проблем:
- Скорее всего, создание файла внутри этой функции — это особый случай, который нужно тестировать отдельно. Повторные запуски тестов перестанут проверять эту ситуацию
- Будет сложно написать предсказуемый тест. Придется дополнительно придумывать неочевидные схемы — например, проверять только последнюю строку в файле. Такой подход понижает качество теста
- Это не особенно критичная проблема, но в процессе запуска тестов появляется файл, который постоянно растет в размерах
При правильной организации тестов каждый тест работает в идентичном окружении на каждом запуске. Чтобы добиться такой организации, можно удалять файл после выполнения каждого теста. В большинстве ситуаций такое решение работает нормально, но все же не во всех.
Выполнение кода тестов — это не атомарная операция. Нет никакой гарантии, что код после тестов выполнится. Есть много причин, по которым этого может не произойти — от внезапного отключения электроэнергии до ошибок в самом Pytest. Мы можем создать pytest-фикстуру для подготовки файла c удалением в конце:
Даже ручное удаление файлов — это сложное решение, которое требует постоянного контроля происходящего. Придется все время об этом помнить. Чтобы не приходилось этим заниматься, программисты опираются на особенности работы файловой системы в Linux — возможность создания временных директорий.
Временные директории
Временные директории в Python можно создавать двумя способами:
- С помощью стандартной библиотеки tempfile
- С помощью фикстуры tmp_path фреймворка pytest или любых других средств тестового фреймворка
После применения создается временная директория с уникальным именем, затем все действия происходят внутри нее. Каждое создание такой директории гарантирует уникальное имя. Удалять такие директории не нужно, потому что операционная система сама подчищает их:
Виртуальная файловая система
Это еще один способ тестировать код, работающий с файловыми системами.
С помощью специальной библиотеки во время тестов создается виртуальная файловая система. Она автоматически подменяет реальную файловую систему для всех модулей, работающих с файловой системой.
Это значит, что тестируемую функцию трогать не надо. Эта функция продолжает думать, что она работает с реальным диском. Вся конфигурация при этом задается снаружи:

