Рекомендуется читать оригинал с лучшим форматированием, чем этот пост на Medium.
"Если вам не нравится модульное тестирование вашего продукта, скорее всего, ваши клиенты тоже не захотят его тестировать". — Аноним
Немногие разработчики приходят в восторг от идеи написания тестов для своего кода. Особенно в связи с необходимостью как можно быстрее закончить новые функции, писать тестовый код, который напрямую не способствует прогрессу проекта, раздражает. Поначалу это может показаться нормальным, когда проект небольшой, и вы можете протестировать несколько функций вручную, чтобы убедиться, что все выглядит нормально, но по мере роста проекта эта ручная проверка становится не только ужасно неэффективной и низкокачественной, но и практически невозможной.
Предварительные инвестиции в тестирование — одна из лучших инвестиций, которые вы можете сделать в своем проекте. Это то, что позволяет вам написать фичу, не трогать ее неделями, вернуться, увидеть, что она проходит все тесты, и иметь определенную степень уверенности, что в мире все хорошо. В этом посте будут рассмотрены некоторые важные концепции тестирования и способы их применения в ваших проектах JavaScript.
Основы тестирования
Принципы
Тесты должны,
- быть простым, кратким и понятным. Хороший тест так же хорош, как и документация, когда дело доходит до понимания того, как использовать библиотеку или кодовую базу.
- опишите, что тестируется, по какому сценарию и каков ожидаемый результат.
- следуйте схеме ААА.
Упорядочить: код, необходимый для настройки сценария, который должен протестировать тест.
Действие: вызовите код, который вы тестируете.
Утверждение: проверьте, соответствует ли полученный результат ожидаемым результатам.
- используйте декларативные утверждения, а не императивные утверждения.
- сосредоточьтесь на поведенческих тестах, то есть тестах, которые проверяют поведение, а не конкретную реализацию. По сути, это сводится к тестированию только общедоступных методов, а не частных методов, которые они могут использовать.
- отдавайте предпочтение заглушкам и шпионам, а не макетам. Макеты сосредоточены на внутренностях службы и, следовательно, тесно связаны с реализацией. С другой стороны, шпионы и заглушки сосредоточены на мониторинге использования службы, а не на том, как она реализована.
- улучшите тестирование входных данных с помощью библиотеки, такой как faker, которая генерирует случайные имена, номера телефонов и т. д., и/или библиотеки тестирования на основе свойств, такой как fast-check, которая генерирует огромное количество входных данных на основе заданных вами входных свойств.
- Избегайте глобальных начальных значений и текстовых фикстур, вместо этого предпочитая добавлять необходимые данные для каждого теста, чтобы они оставались независимыми.
- ожидать ошибок вместо того, чтобы пытаться их перехватывать (например, expect(foo).to.throw(MyError)).
- быть помечены, чтобы такие вещи, как быстрые тесты, выполнялись при сохранении, а более медленные тесты запускались при более крупных событиях, например, перед отправкой.
- стремитесь к покрытию кода ~80%.
- используйте библиотеку тестирования мутаций, такую как Stryker, чтобы убедиться, что тесты, о которых сообщается в отчете о покрытии кода, действительно эффективны.
- используйте тестовые линтеры типа eslint-plugin-jest.
Типы
Статический
Статические тесты запускаются по мере ввода кода.
К ним относятся,
- Линтеры
- Системы типов
- Сканер уязвимых зависимостей
- Анализ сложности кода
- Проверка лицензии
- Проверка на плагиат
Единица измерения
Модульный тест — это тест, проверяющий поведение небольшой единицы кода независимо от остального кода. Эти тесты предназначены не для поиска ошибок, а скорее для обеспечения того, чтобы небольшая часть кода работала должным образом и продолжала работать, даже если может произойти рефакторинг (реструктуризация кода, но не функциональности).
Это короткие, быстрые, очень специфичные области ошибок, быстрые в написании и дешевые, но они обеспечивают минимальную фактическую общую достоверность тестирования для каждого теста и требуют, чтобы многие из них имели хорошее покрытие.
Принципы
F.I.R.S.T.
- Быстро. В проекте могут быть тысячи модульных тестов, поэтому они должны быть быстрыми.
- Независимый: тест должен проверять часть кода, независимую от остальной части проекта.
- Повторяемость: каждый тест должен давать одинаковые результаты каждый раз, пока тестируемый код не изменился. Это означает, что он не может зависеть от конкретных элементов, которые могут меняться, таких как дата/время, запуск системы или любой вывод функции renadom.
- Самопроверка: не требует ручной проверки, чтобы определить, пройден тест или нет.
- Подробно. Должен охватывать все сценарии использования, включая крайние и крайние случаи, исключения/ошибки, неверные входные данные и т. д.
Интеграция
Интеграционные тесты проверяют правильность совместной работы нескольких модулей. Раньше было популярно писать много модульных тестов, но тенденция, похоже, смещается к меньшему количеству модульных тестов и большему количеству интеграционных тестов, поскольку один интеграционный тест может тестировать функциональность, для тестирования которой потребовалось бы несколько модульных тестов. Очевидно, что это приносит в жертву способность иметь детальное знание того, где находится ошибка, когда тест терпит неудачу, но важно найти баланс между экономией времени и степенью специфичности, которую вы имеете.
Обычно они в 4-5 раз больше модульного теста, все еще довольно быстрые, с прилично небольшой областью ошибок, требуют немного больше усилий для написания, не такие дешевые, как модульные тесты, но все же приемлемые, и обеспечивают приличную фактическую общую уверенность при тестировании на тест и выполнение. не требуется столько тестов, чтобы иметь хорошее покрытие.
Концы с концами
Сквозные тесты проверяют полные пользовательские потоки, имитируя действия, которые пользователь фактически будет выполнять.
Это большие, медленные, огромные области ошибок, очень трудоемкие для написания и дорогие для запуска, но они обеспечивают очень высокую надежность и не требуют большого их количества.
Разработка тестового драйвера (TDD)
«Разработка тестов — это не только процесс тестирования, но и один из лучших известных способов предотвращения ошибок. Мышление, которое необходимо сделать для создания полезного теста, может обнаружить и устранить ошибки до того, как они будут закодированы». — Борис Бейзер
Эта методология разработки программного обеспечения направлена на написание более надежного и хорошо спроектированного программного обеспечения путем написания сначала неудачных тестов, а затем написания кода, который обеспечивает их прохождение. Сосредоточившись на написании только того кода, который необходим для выполнения ваших тестов, ваш код должен оставаться коротким и чистым. TDD сосредоточен вокруг 4 основных циклов.
Три закона
- Вы должны написать неудачный тест, прежде чем писать какой-либо производственный код.
- Вы не должны писать больше теста, чем достаточно для того, чтобы он провалился или не смог скомпилироваться.
- Вы не должны писать больше производственного кода, чем достаточно для того, чтобы текущий не пройденный тест прошел.
Эти законы применяются в TDD каждую секунду.
Красный/Зеленый/Рефакторинг
- Создайте модульные тесты, которые терпят неудачу
- Напишите производственный код, который сделает этот тест пройденным.
- Уберите беспорядок, который вы только что сделали.
Эти шаги выполняются поминутно в TDD.
Конкретный/общий
По мере того, как тесты становятся более конкретными, код становится более общим.
Каждые несколько минут вы должны убедиться, что код, который вы пишете, является общим и не специфичным для ваших тестов. Если вы обнаружите, что он становится специфичным для ваших тестов, вы должны вернуться и сделать код более общим.
Границы
Каждый час вы должны останавливаться и проверять, не достигли ли вы существенной архитектурной границы в своем приложении или не пересекли ее. Это дает вам возможность взглянуть на программу на более высоком уровне и спланировать, где вы хотите провести границы, и соответствующим образом сфокусировать свой следующий час циклов TDD.
CI/CD
Непрерывная интеграция (CI)
Непрерывная интеграция — это практика разработки программного обеспечения, заключающаяся в частом внесении небольших изменений в ваш репозиторий кода. Для каждого нажатия должно выполняться автоматическое форматирование и тестирование. Это дает разработчику быстрый цикл обратной связи для определения потенциальных конфликтов в коммитах, а также позволяет часто добавлять новые обновления в приложение.
Непрерывное развертывание (CD)
Также называемое непрерывной доставкой, непрерывное развертывание работает в сочетании с CI, чтобы взять протестированное и созданное приложение, полученное в результате процесса CI, и развернуть (или доставить) его в предполагаемую инфраструктуру. С CD команды могут запускать новый код в производство каждый день или даже ежечасно.
Вывод
Тестирование — сложная и важная концепция в мире программного обеспечения, которую слишком часто отбрасывают в сторону, но с появлением новых практик, таких как CI/CD, наличие надежных тестов важнее, чем когда-либо. Не существует золотого правила написания идеальных тестов, но использование TDD и попытка получить примерно 80% покрытия с помощью комбинации модульных, интеграционных и сквозных тестов должны привести к чистому и надежному коду. Сначала требуется некоторое время для настройки, но уверенность, которую дает вам автоматизированное тестирование, бесценна. Попробуйте концепции из этого поста, и я надеюсь, что это поможет снять напряжение, которое разработчики могут испытывать при программировании.
Первоначально опубликовано на https://thefinnternet.com.