Unit-тесты на фронтенде. Best practices

На своих проектах я использую как 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-тестов максимальную пользу.

Какой была ваша первая зарплата в QA и как вы искали первую работу?

Мега обсуждение в нашем телеграм-канале о поиске первой работы. Обмен опытом и мнения.

6 КОММЕНТАРИИ

Подписаться
Уведомить о
guest

6 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
kuba
kuba
2 лет назад

Хорошие советы, да, сегодня многие пытаются добиваться 100-процентного покрытия юни тестами. Из-за этого часто создаются громадные юниты, которые проверяют то, что должно проверяться интеграцией.

Сергей
Сергей
2 лет назад
Ответить на  kuba

Да, и громадные снэпшоты, буквально на днях видел у себя на проекте снэпшот на 7000+ строк.

JD
JD
2 лет назад
Ответить на  kuba

Вопрос, сколько времени выделяет компания на написание юнитов. Может и тесты такие потому что требуют писать, а время на это не выделяют.
На хорошие юнит тесты требуется около 50% времени, потраченного на разработку фичи.

Программер
Программер
2 лет назад

Кто-нибудь может простым языком объяснить, зачем нужны снэпшот тесты и какую проблему они решают?

Serg
Serg
2 лет назад
Ответить на  Программер

мы почти полностью отказались от Unit-тестов в пользу интеграционных и стало гораздо лучше. Лишняя трата времени, которая очень редко себя оправдывает. Считанные разы unit-тесты спасали от багов. Плюс часто из-за coverage чека unit тесты пишутся от балды.

kuba
kuba
2 лет назад
Ответить на  Программер

Если в двух словах — то помогают определить места, которые изменились в DOM-дереве (и не только в DOM-дереве) после внесения изменений в код.

Подробно можно почитать в официальной документации
https://jestjs.io/ru/docs/snapshot-testing

Мы в Telegram

Наш официальный канал
Полезные материалы и тесты
Готовимся к собеседованию
Project- и Product-менеджмент

? Популярное

? Telegram-обсуждения

Наши подписчики обсуждают, как искали первую работу в QA. Некоторые ищут ее прямо сейчас.
Наши подписчики рассказывают о том, как не бояться задавать тупые вопросы и чувствовать себя уверенно в новой команде.
Обсуждаем, куда лучше податься - в менеджмент или по технической ветке?
Говорим о конфликтных ситуациях в команде и о том, как их избежать
$1100*
медианная зарплата в QA в июне 2023

*по результатам опроса QA-инженеров в нашем телеграм-канале

Собеседование

19%*
IT-специалистов переехало или приняло решение о переезде из России по состоянию на конец марта 2022

*по результатам опроса в нашем телеграм-канале

live

Обсуждают сейчас