HTML Diff
1 added 1 removed
Original 2026-01-01
Modified 2026-02-26
1 <p>Самый типичный побочный эффект - это взаимодействие с файлами. В основном это либо чтение файлов, либо запись в них. В этом уроке вы узнаете, как справиться с такими побочными эффектами при тестировании.</p>
1 <p>Самый типичный побочный эффект - это взаимодействие с файлами. В основном это либо чтение файлов, либо запись в них. В этом уроке вы узнаете, как справиться с такими побочными эффектами при тестировании.</p>
2 <p>С чтением разбираться значительно проще, поэтому с него и начнем.</p>
2 <p>С чтением разбираться значительно проще, поэтому с него и начнем.</p>
3 <h2>Чтение файлов</h2>
3 <h2>Чтение файлов</h2>
4 <p>В большинстве случаев чтение файлов не доставляет особых проблем. Оно ничего не изменяет и выполняется локально, в отличие от сетевых запросов. Это значит, что при наличии необходимого файла и нужных прав, вероятность случайных ошибок крайне низка.</p>
4 <p>В большинстве случаев чтение файлов не доставляет особых проблем. Оно ничего не изменяет и выполняется локально, в отличие от сетевых запросов. Это значит, что при наличии необходимого файла и нужных прав, вероятность случайных ошибок крайне низка.</p>
5 <p>При тестировании функций, читающих файлы, должно выполняться ровно одно условие - функция должна позволять менять путь до файла. В таком случае достаточно создать файл нужной структуры в фикстурах:</p>
5 <p>При тестировании функций, читающих файлы, должно выполняться ровно одно условие - функция должна позволять менять путь до файла. В таком случае достаточно создать файл нужной структуры в фикстурах:</p>
6 - <p>Но в тестах читать<em>/etc/passwd</em>нельзя, потому что содержимое этого файла ависит от системного окружения, в котором запущены тесты. Потому для тестирования нужно создать файл аналогичной структуры в фикстурах и указать его при запуске функции:</p>
6 + <p>Но в тестах читать<em>/etc/passwd</em>нельзя, потому что содержимое этого файла зависит от системного окружения, в котором запущены тесты. Потому для тестирования нужно создать файл аналогичной структуры в фикстурах и указать его при запуске функции:</p>
7 <h2>Запись файлов</h2>
7 <h2>Запись файлов</h2>
8 <p>С записью файлов уже сложнее. Главная проблема - это отсутствие гарантированной идемпотентности. Это значит, что повторный вызов функции, записывающей файлы, может вести себя не как первый вызов. Например, он может завершаться с ошибкой или приводить к другим результатам.</p>
8 <p>С записью файлов уже сложнее. Главная проблема - это отсутствие гарантированной идемпотентности. Это значит, что повторный вызов функции, записывающей файлы, может вести себя не как первый вызов. Например, он может завершаться с ошибкой или приводить к другим результатам.</p>
9 <p>Разберемся, почему так происходит. Представьте, что мы пишем тесты на функцию log(message), которая дописывает все переданные в нее сообщения в файл:</p>
9 <p>Разберемся, почему так происходит. Представьте, что мы пишем тесты на функцию log(message), которая дописывает все переданные в нее сообщения в файл:</p>
10 <p>Это значит, что каждый запуск тестов будет немного другим. При первом запуске тестов создается файл для хранения логов. Затем он начнет заполняться. Это приводит к целой пачке проблем:</p>
10 <p>Это значит, что каждый запуск тестов будет немного другим. При первом запуске тестов создается файл для хранения логов. Затем он начнет заполняться. Это приводит к целой пачке проблем:</p>
11 <ul><li>Скорее всего, создание файла внутри этой функции - это особый случай, который нужно тестировать отдельно. Повторные запуски тестов перестанут проверять эту ситуацию</li>
11 <ul><li>Скорее всего, создание файла внутри этой функции - это особый случай, который нужно тестировать отдельно. Повторные запуски тестов перестанут проверять эту ситуацию</li>
12 <li>Будет сложно написать предсказуемый тест. Придется дополнительно придумывать неочевидные схемы - например, проверять только последнюю строку в файле. Такой подход понижает качество теста</li>
12 <li>Будет сложно написать предсказуемый тест. Придется дополнительно придумывать неочевидные схемы - например, проверять только последнюю строку в файле. Такой подход понижает качество теста</li>
13 <li>Это не особенно критичная проблема, но в процессе запуска тестов появляется файл, который постоянно растет в размерах</li>
13 <li>Это не особенно критичная проблема, но в процессе запуска тестов появляется файл, который постоянно растет в размерах</li>
14 </ul><p>При правильной организации тестов каждый тест работает в идентичном окружении на каждом запуске. Чтобы добиться такой организации, можно удалять файл после выполнения каждого теста. В большинстве ситуаций такое решение работает нормально, но все же не во всех.</p>
14 </ul><p>При правильной организации тестов каждый тест работает в идентичном окружении на каждом запуске. Чтобы добиться такой организации, можно удалять файл после выполнения каждого теста. В большинстве ситуаций такое решение работает нормально, но все же не во всех.</p>
15 <p>Выполнение кода тестов - это не атомарная операция. Нет никакой гарантии, что код после тестов выполнится. Есть много причин, по которым этого может не произойти - от внезапного отключения электроэнергии до ошибок в самом Pytest. Мы можем создать pytest-фикстуру для подготовки файла c удалением в конце:</p>
15 <p>Выполнение кода тестов - это не атомарная операция. Нет никакой гарантии, что код после тестов выполнится. Есть много причин, по которым этого может не произойти - от внезапного отключения электроэнергии до ошибок в самом Pytest. Мы можем создать pytest-фикстуру для подготовки файла c удалением в конце:</p>
16 <p>Даже ручное удаление файлов - это сложное решение, которое требует постоянного контроля происходящего. Придется все время об этом помнить. Чтобы не приходилось этим заниматься, программисты опираются на особенности работы файловой системы в Linux - возможность создания<strong>временных директорий</strong>.</p>
16 <p>Даже ручное удаление файлов - это сложное решение, которое требует постоянного контроля происходящего. Придется все время об этом помнить. Чтобы не приходилось этим заниматься, программисты опираются на особенности работы файловой системы в Linux - возможность создания<strong>временных директорий</strong>.</p>
17 <h2>Временные директории</h2>
17 <h2>Временные директории</h2>
18 <p>Временные директории в Python можно создавать двумя способами:</p>
18 <p>Временные директории в Python можно создавать двумя способами:</p>
19 <ul><li>С помощью стандартной библиотеки<a>tempfile</a></li>
19 <ul><li>С помощью стандартной библиотеки<a>tempfile</a></li>
20 <li>С помощью фикстуры<a>tmp_path</a>фреймворка pytest или любых других средств тестового фреймворка</li>
20 <li>С помощью фикстуры<a>tmp_path</a>фреймворка pytest или любых других средств тестового фреймворка</li>
21 </ul><p>После применения создается временная директория с уникальным именем, затем все действия происходят внутри нее. Каждое создание такой директории гарантирует уникальное имя. Удалять такие директории не нужно, потому что операционная система сама подчищает их:</p>
21 </ul><p>После применения создается временная директория с уникальным именем, затем все действия происходят внутри нее. Каждое создание такой директории гарантирует уникальное имя. Удалять такие директории не нужно, потому что операционная система сама подчищает их:</p>
22 <h2>Виртуальная файловая система</h2>
22 <h2>Виртуальная файловая система</h2>
23 <p>Это еще один способ тестировать код, работающий с файловыми системами.</p>
23 <p>Это еще один способ тестировать код, работающий с файловыми системами.</p>
24 <p>С помощью<a>специальной библиотеки</a>во время тестов создается виртуальная файловая система. Она автоматически подменяет реальную файловую систему для всех модулей, работающих с файловой системой.</p>
24 <p>С помощью<a>специальной библиотеки</a>во время тестов создается виртуальная файловая система. Она автоматически подменяет реальную файловую систему для всех модулей, работающих с файловой системой.</p>
25 <p>Это значит, что тестируемую функцию трогать не надо. Эта функция продолжает думать, что она работает с реальным диском. Вся конфигурация при этом задается снаружи:</p>
25 <p>Это значит, что тестируемую функцию трогать не надо. Эта функция продолжает думать, что она работает с реальным диском. Вся конфигурация при этом задается снаружи:</p>
26  
26