testengineer.ru

Домой Обучение Unit-тестирование Unit-тесты на фронтенде. Best practices

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

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

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

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

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

ОСТАВЬТЕ ОТВЕТ

Пожалуйста, введите ваш комментарий!
пожалуйста, введите ваше имя здесь

Последние публикации

Последние комментарии