- Разберемся, что не нужно тестировать
- Разумное использование снапшотов
- Делайте ваши тесты понятными
- Краткость — сестра таланта
- Моки ответов сервера
- Автоматизация
На своих проектах я использую как unit, так и интеграционные тесты. В последние пару месяцев я написал много unit-тестов для React компонентов и хочу поделиться некоторыми простыми концепциями. Они помогут вам писать правильные unit-тесты, которые будет легко поддерживать.
Названия функций и примеры ниже написаны на Jest и RTL (RTL — React testing library), но концепции применимы к любой другой библиотеке для тестирования фронтенда.
Разберемся, что не нужно тестировать
Самый важный мой совет не про то, как тестировать, а про то, что тестировать не нужно. Он применим к ситуациям, когда на вашем проекте есть или планируются E2E интеграционные тесты. Важно понимать, что многие use-кейсы могут (и должны) покрываться интеграционными тестами, а не unit-тестами.
Не все фичи могут быть протестированы интеграционными тестами. Например, если для теста нужно создавать 3-4 мока, во время теста изменяется состояние приложения — лучше делать интеграцию. Но для простых тестов атомарной функциональности unit-тестирование подойдет идеально. Вот еще пара примеров:
❌ Пример плохого сценария для unit-теста:
- Проверка формы логина (запросы и ответы от сервера, возможность увидеть следующую страницу после логина в приложение)
✅ Примеры хороших сценариев для unit-теста:
- Добавление нового <option> в <select> и проверка того, что он показывается
- Добавление click ивента на кнопку и проверка того, что вызывается callback
Разумное использование снапшотов
Снапшот тестирование помогает отслеживать неожиданные изменения в компонентах. Но не нужно путать снапшот-тестирование с нормальными функциональными тестами.

Правильный сценарий использования снапшотов — отслеживать изменения в shared-компонентах. Когда в такой компонент вносятся изменения — снапшоты покажут все места, на которые эти изменения повлияли. Но это единственное, что делают снапшоты. После того, как снапшоты упали, вам все равно придется вручную проверить все эти места и убедиться, что изменение ничего не сломало.
Делайте ваши тесты понятными
Обязанность разработчика — писать чистый и понятный код, который будет легко читать другим разработчикам.
У Jest классный синтаксис, который помогает делать тесты легко читаемыми и понятными. Используйте его!
❌ Пример непонятного теста:
describe('component', () => {
it('performs correctly', () => {
…
});
});
✅ Пример понятного теста:
describe('the admin page', () => {
describe('when a user is not logged in', () => {
it('shows a login button', () => {
…
});
});
});
Обратите внимание, в последнем примере тест читается, как нормальное предложение. Когда тест упадет, вам сразу будет понятно, что конкретно не работает и в каком направлении копать.
Краткость — сестра таланта
Делайте каждый ваш тест настолько маленьким, насколько это возможно. Придерживайтесь принципов DRY, вон несколько правил, которым нужно следовать:
- Если несколько тестов используют один и тот же код, выносите его в beforeEach и afterEach.
- Когда нужно тестировать один и тот же компонент, рендерите его один раз в beforeEach.
- Если какие-нибудь значения используются и в компоненте и в тесте, выносите их в константы и импортируйте в тест и компонент.
- Если один и тот же мок используется в нескольких тестах, выносите его в отдельное место и импортируйте в ваши тесты.
- Используйте ID вместо привязки к структуре DOM или к тексту внутри компонентов. Структура dom-дерева и текст, в отличие от id, могут часто меняться. Если вы хотите использовать другой формат (вместо data-testid, который рекомендует RTL), его можно сменить в конфиге RTL. Я, например, использую data-test.
Моки ответов сервера
Моки для ответов API позволяют протестировать компонент, подключенный к данным. После появления хуков, стало гораздо проще вынести логику асинхронных запросов из компонента и, соответственно, писать моки для компонентов.
Я использовал библиотеку @react-mock/fetch. С ее помощью можно очень легко сделать мок для HTTP-запросов, данные от которых получает компонент. Нужно просто обернуть ваш компонент в <FetchMock> и валя — данные внутри компонента:
import { FetchMock } from '@react-mock/fetch';
const mockedResponse = {
matcher: '/ay',
method: 'GET',
response: JSON.stringify({ body: 'yo' })
};
render(
<FetchMock options={mockedResponse}>
<MyComponent />
</FetchMock>
);
Автоматизация
Вот, как я запускаю свои тесты:
- Использую pre-push хук Husky, который запускает тесты перед пушем в репозиторий
- Jenkins merge build прогоняет тесты в каждом PR перед тем, как смержить его в главную ветку
Вам решать, как организовать CI, но рекомендую сделать хотя бы что-то одно из тех двух опций выше. Это поможет получить от unit-тестов максимальную пользу.
Хорошие советы, да, сегодня многие пытаются добиваться 100-процентного покрытия юни тестами. Из-за этого часто создаются громадные юниты, которые проверяют то, что должно проверяться интеграцией.
Да, и громадные снэпшоты, буквально на днях видел у себя на проекте снэпшот на 7000+ строк.
Я писал 10к строк юнитов, это применимо в маленких проектах, где ты за месяц, помимо интеграции можешь себе позволить писать юниты. Ноль проблем вообще, пишутся быстро, разрабы благодарят, в пайплайне проганяются за секунды.
Вопрос, сколько времени выделяет компания на написание юнитов. Может и тесты такие потому что требуют писать, а время на это не выделяют.
На хорошие юнит тесты требуется около 50% времени, потраченного на разработку фичи.
Кто-нибудь может простым языком объяснить, зачем нужны снэпшот тесты и какую проблему они решают?
мы почти полностью отказались от Unit-тестов в пользу интеграционных и стало гораздо лучше. Лишняя трата времени, которая очень редко себя оправдывает. Считанные разы unit-тесты спасали от багов. Плюс часто из-за coverage чека unit тесты пишутся от балды.
Если в двух словах — то помогают определить места, которые изменились в DOM-дереве (и не только в DOM-дереве) после внесения изменений в код.
Подробно можно почитать в официальной документации
https://jestjs.io/ru/docs/snapshot-testing