0 added
0 removed
Original
2026-01-01
Modified
2026-02-26
1
<p>Наиболее типичный побочный эффект - взаимодействие с файлами (файловые операции). В основном это либо чтение файлов, либо запись в них. С чтением разбираться значительно проще, поэтому с него и начнем.</p>
1
<p>Наиболее типичный побочный эффект - взаимодействие с файлами (файловые операции). В основном это либо чтение файлов, либо запись в них. С чтением разбираться значительно проще, поэтому с него и начнем.</p>
2
<h2>Чтение файлов</h2>
2
<h2>Чтение файлов</h2>
3
<p>В большинстве случаев чтение файлов не доставляет особых проблем. Оно ничего не изменяет и выполняется локально, в отличие от сетевых запросов. Это значит, что мы вряд ли столкнемся со случайными ошибками, если у нас есть необходимый файл и нужные права.</p>
3
<p>В большинстве случаев чтение файлов не доставляет особых проблем. Оно ничего не изменяет и выполняется локально, в отличие от сетевых запросов. Это значит, что мы вряд ли столкнемся со случайными ошибками, если у нас есть необходимый файл и нужные права.</p>
4
<p>При тестировании функций, читающих файлы, должно выполняться ровно одно условие. Функция должна позволять менять путь до файла. В таком случае, достаточно создать файл нужной структуры в фикстурах.</p>
4
<p>При тестировании функций, читающих файлы, должно выполняться ровно одно условие. Функция должна позволять менять путь до файла. В таком случае, достаточно создать файл нужной структуры в фикстурах.</p>
5
<p>В тестах читать<em>/etc/passwd</em>нельзя, потому что содержимое этого файла зависит от окружения, в котором запущены тесты. Для тестирования нужно создать файл аналогичной структуры в фикстурах и указать его при запуске функции:</p>
5
<p>В тестах читать<em>/etc/passwd</em>нельзя, потому что содержимое этого файла зависит от окружения, в котором запущены тесты. Для тестирования нужно создать файл аналогичной структуры в фикстурах и указать его при запуске функции:</p>
6
<h2>Запись файлов</h2>
6
<h2>Запись файлов</h2>
7
<p>С записью файлов уже сложнее. Главная проблема - отсутствие гарантированной идемпотентности. Это значит, что повторный вызов функции, записывающей файлы, может вести себя не как первый вызов, например, завершаться с ошибкой, либо приводить к другим результатам.</p>
7
<p>С записью файлов уже сложнее. Главная проблема - отсутствие гарантированной идемпотентности. Это значит, что повторный вызов функции, записывающей файлы, может вести себя не как первый вызов, например, завершаться с ошибкой, либо приводить к другим результатам.</p>
8
<p>Почему? Представьте себе, что мы пишем тесты на функцию log(message), которая дописывает все переданные в нее сообщения в файл:</p>
8
<p>Почему? Представьте себе, что мы пишем тесты на функцию log(message), которая дописывает все переданные в нее сообщения в файл:</p>
9
<p>Это значит, что каждый запуск тестов будет немного другим. При первом запуске тестов создается файл для хранения логов. Затем он начнет заполняться. Это приводит к целой пачке проблем:</p>
9
<p>Это значит, что каждый запуск тестов будет немного другим. При первом запуске тестов создается файл для хранения логов. Затем он начнет заполняться. Это приводит к целой пачке проблем:</p>
10
<ul><li>Наверняка внутри этой функции процесс создания файла это особый случай, который нужно тестировать отдельно. Повторные запуски тестов перестанут проверять эту ситуацию.</li>
10
<ul><li>Наверняка внутри этой функции процесс создания файла это особый случай, который нужно тестировать отдельно. Повторные запуски тестов перестанут проверять эту ситуацию.</li>
11
<li>Сложнее написать предсказуемый тест. Придется дополнительно придумывать хитрые схемы, например проверять только последнюю строку в файле. Такой подход понижает качество теста.</li>
11
<li>Сложнее написать предсказуемый тест. Придется дополнительно придумывать хитрые схемы, например проверять только последнюю строку в файле. Такой подход понижает качество теста.</li>
12
<li>Не особенно критично, но все же: в процессе запуска тестов появляется файл, который постоянно растет в размерах.</li>
12
<li>Не особенно критично, но все же: в процессе запуска тестов появляется файл, который постоянно растет в размерах.</li>
13
</ul><p>При правильной организации тестов, каждый тест работает в идентичном окружении на каждом запуске. Для этого, например, можно удалять файл после выполнения каждого теста. В Jest есть хук afterEach который выполняется после каждого теста. Эту задачу можно попробовать решить с его помощью:</p>
13
</ul><p>При правильной организации тестов, каждый тест работает в идентичном окружении на каждом запуске. Для этого, например, можно удалять файл после выполнения каждого теста. В Jest есть хук afterEach который выполняется после каждого теста. Эту задачу можно попробовать решить с его помощью:</p>
14
<p>В большинстве ситуаций такое решение работает нормально, но все же не во всех. Выполнение кода тестов - это не атомарная операция. Нет никакой гарантии, что хук afterEach() выполнится. Есть много причин, по которым это может не произойти, например, из-за самого Jest.</p>
14
<p>В большинстве ситуаций такое решение работает нормально, но все же не во всех. Выполнение кода тестов - это не атомарная операция. Нет никакой гарантии, что хук afterEach() выполнится. Есть много причин, по которым это может не произойти, например, из-за самого Jest.</p>
15
<p>Есть только один надежный способ делать очистку - делать это до теста, а не после, в beforeEach(). С таким подходом есть только одна небольшая сложность. При первом запуске тестов файла нет. Это значит, что прямой вызов unlink() завершится с ошибкой и тесты не смогут выполниться. Чтобы избежать этого, можно подавить ошибку:</p>
15
<p>Есть только один надежный способ делать очистку - делать это до теста, а не после, в beforeEach(). С таким подходом есть только одна небольшая сложность. При первом запуске тестов файла нет. Это значит, что прямой вызов unlink() завершится с ошибкой и тесты не смогут выполниться. Чтобы избежать этого, можно подавить ошибку:</p>
16
<p>Другой вопрос при записи файлов - куда их сохранять? Однозначно избегайте записи файлов прямо внутри проекта. Если тестируемый код позволяет сконфигурировать место записи, то используйте системную временную директорию. Ее можно получить через модуль<em>os</em>:</p>
16
<p>Другой вопрос при записи файлов - куда их сохранять? Однозначно избегайте записи файлов прямо внутри проекта. Если тестируемый код позволяет сконфигурировать место записи, то используйте системную временную директорию. Ее можно получить через модуль<em>os</em>:</p>
17
<h2>Виртуальная файловая система (ФС)</h2>
17
<h2>Виртуальная файловая система (ФС)</h2>
18
<p>Это еще один способ тестировать код, работающий с ФС. С помощью<a>специальной библиотеки</a>во время тестов создается виртуальная файловая система. Она автоматически подменяет реальную файловую систему для модуля<em>fs</em>. Это значит, что функцию, которая тестируется, трогать не надо. Эта функция продолжает думать, что она работает с реальным диском. Вся конфигурация при этом задается снаружи:</p>
18
<p>Это еще один способ тестировать код, работающий с ФС. С помощью<a>специальной библиотеки</a>во время тестов создается виртуальная файловая система. Она автоматически подменяет реальную файловую систему для модуля<em>fs</em>. Это значит, что функцию, которая тестируется, трогать не надо. Эта функция продолжает думать, что она работает с реальным диском. Вся конфигурация при этом задается снаружи:</p>
19
<p>Этот способ дает идемпотентность из коробки. Вызов функции mock формирует окружение на каждый запуск с нуля. То есть достаточно добавить ее в beforeEach и можно приступать к тестированию.</p>
19
<p>Этот способ дает идемпотентность из коробки. Вызов функции mock формирует окружение на каждый запуск с нуля. То есть достаточно добавить ее в beforeEach и можно приступать к тестированию.</p>