Написать пост

Какие инструменты и подходы использовать для тестирования фронтенда — рассказывают эксперты

Аватар Никита Прияцелюк

Во фронтенд-тестировании существуют разные подходы и инструменты, в которых легко запутаться. Узнаём у экспертов, что они рекомендуют использовать.

Тестирование — достаточно надёжный способ отловить ошибки ещё на этапе разработки и сохранить нервы в процессе эксплуатации ПО. Описание целей и процесса тестирования фронтенда изложено в таких наших материалах: знакомство с фронтенд-тестированием, юнит-тестирование, интеграционное и E2E-тестирование. А в этой статье эксперты расскажут, какие инструменты и подходы для фронтенд-тестирования они рекомендуют использовать веб-разработчику и тестировщику.

Когда идёт речь об автоматизации тестирования фронтенда, самым очевидным инструментом является Selenium. Хотя, если проект непродолжительный по времени, стоит в качестве альтернативы рассмотреть Selenide или другую обёртку. Это позволит заметно ускорить старт проекта и избежать написания под себя функций для базового взаимодействия. Затем очень важно создать удобную инфраструктуру, где можно было бы свести к минимуму все ручные операции и наглядно представить результаты тестов. В этом может помочь связка из Allure, Jenkins и Selenoid. Allure можно использовать как для общей статистики, так и для отражения в отчёте всех шагов и действий тестов. Для этого надо лишь снабдить код необходимыми аннотациями. Selenoid нужен для удалённого запуска ваших UI тестов, так как после определённого момента запускать их локально становиться непозволительно по времени. Ну и наконец Jenkins служит как связующее звено и позволяет с лёгкость настроить запуск тестов в определённое время или после очередной сборки фронта.

В качестве общего подхода при написании тестов могу посоветовать максимальную фрагментацию. Нет никакой необходимости пытаться проверить всё в одном тесте. Напротив, стоит дробить их на небольшие тесты, проверяя по одной-две бизнес функции. И как показывает практика, в сложных приложениях очень удобно перед UI тестом подготовить тестовые данные при помощи автоматизации работы с базой или с API приложения.

И в качестве совета начинающим специалистам, которые решили изучать автоматизацию тестирования фронта при помощи Selenium, могу порекомендовать выучить Xpath, а не пытаться пользоваться программами для автоматического поиска локаторов, и как можно лучше изучить выбранный вами язык программирования, ведь в сложных системах и сложных тестах на первое место уже встаёт работа с данными, и вы не сможете обойтись даже прекрасными знаниями одного лишь Selenium.

Большинство нагрузочных тестов и функциональных автотестов web-приложений в КРОК мы создаём с помощью LoadRunner от MicroFocus. Оптимальным этот инструмент считают и многие внешние ИТ-команды, которые приходят к нам за экспертизой в области разработки автотестов.

Кстати, мало кто знает про возможности LoadRunner в области функционального тестирования, так как привыкли воспринимать его исключительно как инструмент нагрузки. Здесь можно создавать функциональные тесты для приложений, используя протокол TrueClient, а после конвертировать их в нагрузочные. Очень удобная штука, но требует определённых знаний и продуманного подхода к решению.

По факту LoadRunner — огромная профессиональная студия, которая делает много работы за тебя, например помогает быстро создавать нагрузочные скрипты используя автокорреляцию. С её помощью при разработке функциональных тестов можно быстро и качественно создавать локаторы на объекты, встраивать в автотесты свой код на различных языках программирования. Возможность подключать собственные библиотеки — одна из самых сильных сторон этого инструмента. А всё, что не может сделать LoadRunner, можно написать самому на С или JavaScript. Например, это было полезно, когда в нагрузочном тесте нам нужно было проверить систему, где каждый отчёт, отправляемый на сервер, должен сопровождаться электронной подписью. По LoadRunner много информации в интернете на тематических форумах, есть видеопрезентации в YouTube. Обычно достаточно часа, чтобы подключить нового человека к созданию или поддержке автотестов.

Раньше LoadRunner был полностью платный, а сейчас бесплатен для нагрузки 50 пользователями, протокол TrueClient же вообще бесплатен. Обычно полсотни пользователей достаточно, чтобы показать разработчикам их слабые места в коде, но всегда можно незначительно увеличить нагрузку, используя виртуализацию.

Каждый программист несёт ответственность за качество написанного им кода, а одной из важнейших характеристик качества является покрытие кода тестами. На практике этой областью часто пренебрегают из-за нехватки времени или дедлайнов, отдавая предпочтение написанию новой функциональности и скидывая эту задачу на тестировщиков. Как результат — на еженедельные спринты накладывается печать боли под названием «исправление багов» из-за непредвиденных ошибок в коде, а любимый Agile постепенно превращается в водопадную модель.

При создании большого SPA со сложной архитектурой написание тестов является неотъемлемой частью процесса разработки. В зависимости от специфики приложения покрытие тестами может быть направлено на работу с данными или на область работы с DOM. Если модуль представляет простой блок отрисовки данных, связанных с API (форма, список, модальное окно или таблица), стоит сконцентрироваться на описании тестами функций, отвечающих за изменение данных, — работы с состоянием или хранилищем данных. Чем сложнее визуальная часть приложения, тем большего внимания заслуживает процесс отрисовки DOM-дерева.

Основные виды тестирования для frontend — Unit-тестирование, интеграционное тестирование, end-to-end (E2E) тестирование. Unit-тест тестирует работу одного юнита (функции, объекта, класса, модуля) в изоляции от других частей приложения. Интеграционные тесты проверяют несколько связанных между собой модулей. Чаще всего для фронтенда это означает проверку работоспособности частей приложения, связанных с внешним миром (работа с localStorage или cookies, работа с API), либо имитацию событий на странице и переходы между модулями. В end-to-end тестировании загружается всё приложение целиком и имитируется работа пользователя с приложением.

Для определения количества тестов каждой из групп существует пирамида тестирования, группирующая необходимые тесты по разным уровням реализации (много Unit-тестов, меньше интеграционных и немного E2E). На практике эта пирамида не всегда является лучшим подходом. Дело в том, что в быстро развивающемся приложении функциональность часто изменяется, бизнес-требования расширяются, при этом затрагивая написанные до этого модули, а некоторые фичи и вовсе могут не дойти до деплоя в production. Поэтому покрывать всю функциональность приложения тестами не всегда имеет смысл. Пирамида в таком случае может встать с ног на голову.

В разработке я уделяю больше всего внимания написанию интеграционных тестов, в частности работе с API. Это не самая динамичная часть кода, а повышение отказоустойчивости при работе с сервером — одна из важнейших целей тестирования фронтенда.

Для реализации эффективного тестирования существуют TDD (Test Driven Development) и BDD (Behavior Driven Development). Это специальные техники экстремального программирования, когда сначала разработчик пишет тесты, а затем код, который будет удовлетворять заложенные технические требования или пользовательские сценарии. В реальной жизни этот подход эффективен, но сложен в поддержании и внедрении, к примеру, если у вас распределённая команда разработки.

Популярными библиотеками тестирования для фронтенд-разработчика являются Jest для интеграционных и юнит-тестов и Puppeteer или Cypress для end-to-end тестирования. При работе с Angular чаще всего для написания тестов используются Karma и Jasmine, а также Protractor для end-to-end. Помимо общих библиотек каждый фреймворк имеет специфичные подходы к тестированию. Например React содержит test-utils, упрощающие тестирование компонентов. При выборе инструмента рекомендуется изучить подходы и документацию для тестирования вашего фреймворка.

Тестирование — неотъемлемый элемент обеспечения качества (QA). Его объёмы, как это ни банально прозвучит, зависят от требуемого качества: при создании прототипа достаточно проверки результата самим разработчиком, при написании же библиотеки, которая будет использоваться в неизвестном заранее окружении, уже стоит подумать о высокой плотности покрытия юнит-тестами.

Тестирование можно разделить на статическое и динамическое. К статическому относятся автоматические анализаторы кода, и в мире фронтенда стандартом стал ESLint. Для него есть множество наборов настроек, например Airbnb CodeStyle. В нашей компании мы используем собственный пресет.

Стоит сказать и про использование систем типизации, среди которых лидирует TypeScript. Мы пока не видим для себя заметной выгоды от их использования и продолжаем писать на JavaScript. Процесс Code Review также можно назвать одним из видов статического тестирования.

Тема динамического тестирования шире, нельзя не упомянуть ручные и автоматизированные функциональные тесты, но подробнее я остановлюсь на юнит-тестировании с выдержками из рекомендаций для наших фронтенд-разработчиков:

  1. Используйте Jest.
  2. Тесты бывают black box (не знают о реализации) и white box (знают о реализации). Старайтесь писать black box, тестируя вход и выход юнита и не привязываясь к конкретной реализации. У каждого юнита есть публичный интерфейс, контракт, тестируйте его.
  3. Следуйте TDD. Это позволит писать более компактные юниты с достаточным количеством и качеством тестов. Если писать сначала код — с заметной вероятностью его нужно будет переписать в процессе написания тестов.
  4. Размер юнита может быть больше одной функции/метода. Например в Vue-приложении это обычно весь компонент.
  5. Не тестируйте приватные методы и не мокайте их, если в этом нет необходимости. Тест станет более «интеграционным», но не привяжется к деталям реализации.
  6. Не пишите тесты ради тестов.
  7. Не гонитесь за code coverage. Даже 20% может быть вполне достаточно.
  8. Не тестируйте фреймворки, только свою логику.
  9. Тест должен тестировать что-то одно.
  10. Каждый багфикс — повод одновременно с исправлением написать новый тест.

Обычно для тестирования фронтенда мы используем Selenium, Cypress, фреймворки Jasmin или Mocha. Из полезного: PhantomCSS — запоминает состояние веб страницы, позволяя отслеживать регрессии (вроде пропавшей кнопки или другого элемента). Из подходов мы используем метод TDD (Test Driven Development — разработка через тестирование). Из сервисов для тестирования — BrowserStack, для кроссбраузерного тестирования на реальных устройствах. Много полезной информации frontend-специалист может почерпнуть на профессиональных форумах и блогах, типа Habr и в зарубежных источниках.

Для тестирования фронтенда применяются юнит тесты, интеграционные тесты и сквозное тестирование.

Юнит тестирование используется для изолированной проверки компонентов, модулей и различных функций. Например популярным фреймворком для JavaScript является Jest. Он хорошо документирован, прост в установке и использовании, постоянно развивается и имеет богатый набор возможностей, таких как тестирование снимками, простота создания всех видов имитаций и т. д. Для тестирования компонентов на React хорошо подходит набор утилит Enzyme, который позволяет работать с отрисовкой компонентов, имеет удобный API для работы с элементами и также хорошо документирован.

Интеграционные тесты проверяют взаимодействие двух и более модулей между собой. Сквозные (end-to-end) тесты охватывают проверку системы развёрнуто, что предполагает реальный запуск проекта в связке с бэкендом. Для этих целей хорошо подходит платформа Cypress. Она имеет простой синтаксис написания тестов, удобный формат вывода сообщений и понятную документацию, что позволяет даже новичку быстро разбираться с написанием тестовых сценариев.

В современной фронтенд-разработке очень много зависит от выбранного стека технологий, на котором пишется фронтенд. Крупные проекты пишутся на каком-нибудь фрейворке типа React или Vue, а менее крупные проекты — либо на чистом JS, либо на микрофреймворках типа jQuery. В зависимости от выбранного подхода в разработке, тестирование различается. Как правило, в полноценных фреймворках есть уже встроенные механизмы тестирования. Например, вот для React, а вот — для Angular. Если фреймворка нет, то есть такой вариант.

Если смотреть на подходы к автоматическому тестированию фронтенда, то можно выделить:

  • unit тесты — их пишут сами разработчики, используя механизмы, приведенные выше;
  • тестирование вёрстки и UI тесты (эмулирование пользователя) пишут тестировщики-автоматизаторы, так как для прогона таких тестов нужно создавать специально окружение и поддерживать его.

Для UI тестов обычно применяется Selenium. Для тестирования вёрстки и простого тестирования UI применяется метод на основе snapshot.

Активно используем юнит-тесты как способ проверить работоспособность какой-либо малой функциональной единицы и модульные тесты для проверки работоспособности готового логического блока. Применяем как на начальном этапе разработки, так и при пересмотре механики работы элементов интерфейса. Использование связки TypeScript + Jest покрывает потребность в тестировании пользовательских интерфейсов сервисов Containerum Managed Kubernetes, DBaaS, облачной технологической платформы. К визуальному регрессу только присматриваемся — ещё не возникало потребности в использовании в виду высокой динамики разработки и изменений. E2E и интеграционные тесты также представляют интерес.

Всё тестирование программного обеспечения (ПО) можно условно разделить на две большие категории: автоматизированное тестирование (автотесты) и ручное (мануальное). С точки зрения самих разработчиков, когда речь заходит о тестировании ПО, то подразумеваются именно автотесты.

Автоматизированное тестирование кода позволяет снизить количество ручного тестирования, облегчает интеграцию новых фич и помогает в рефакторинге исходного кода.

Говоря об автотестах на фронтенде, как правило, подразумевают 3 вида: юнит-тесты, интеграционные тесты и E2E-тесты.

Юнит-тестирование, оно же модульное — это когда вы тестируете отдельные модули вашего приложения независимо друг от друга. Понятие модуль в данном контексте крайне широкое. Это может быть отдельный класс, функция, компонент и т. п. Важно лишь то, что вы тестируете все эти сущности по возможности независимо друг от друга. Это позволяет не только проверить корректность работы приложения на низком уровне, но и повышает качество кода, так как юнит-тесты налагают определённые требования на сам тестируемый код и его окружение.

Интеграционное тестирование — это уже следующий этап тестирования, когда модули приложения объединяются в группы и тестируются совместно. Цель этого этапа тестирования проста — понять, что могло сломаться при объединении модулей. При этом приложение запускается не в реальном окружении т. е. не используется продуктовый API, а вместо него подключают моки.

E2E-тестирование (системное тестирование) — это третий этап тестирования, выполняемый в полном окружении, максимально приближенном к продуктовому окружению. При этом внутреннее устройство приложения остаётся скрытым от E2E-тестов.

Существует довольно много различных фреймворков для написания тестов, но самыми популярными являются: Mocha, Jasmine и Jest. Также удобно бывает использовать отдельную среду для запуска тестов в разных браузерах, например Karma. Для взаимодействия с браузером используют специальный драйвер типа Selenium и иногда прослойки типа Protractor. Есть и интегрированные среды, которые объединяют в себе все типы инструментов, такие как Cypress.

Для изучения всех эти инструментов могу порекомендовать в первую очередь документацию и GitHub, так как там всегда содержится самая актуальная информация.

На egghead.io и Pluralsight есть множество курсов и лекций по тестированию на фронтенде.

Мне кажется, сообщество разработчиков находится где-то в фазе понимания, что «бетонированию» тестами поддаются только конструкции, обещающие жить долго без изменений. В то же время мы видим повсеместное применение Agile подходов к разработке, намекающих нам на то, что подвижность, гибкость продукта во всех смыслах является одним из ключевых факторов его выживаемости на рынке.

TDD (test-driven development или разработка через тестирование) звучит бодро, когда вы делаете лабораторную работу, и совсем не так, когда вы оказываетесь на рынке и проигрываете тендер только потому, что заложили в цену тесты, актуальность которых едва ли доживёт до релиза. Очевидное противоречие между абстрактным идеальным кодом и бизнесом, который хочет дёшево, надёжно и вчера, решается в таком компромиссе: дешёвых тестов можно много, дорогих — мало. Чем больший объём функциональности покрыт тестом, тем обычно дороже стоит такой тест.

Именно так мы выходим на классику пирамиды тестов: Jest, Mocha, Jasmine и другие в юнит и интеграционных тестах; Karma, Protractor, Phantom и пр. для тестов UI компонент (которые с лёгкой подачи команды Angular стали называть E2E тестами, хотя это не всегда так). Pact как тестирование исполнения контракта между провайдером и потребителем.

Стоит отдельно упомянуть тестирование на безопасность (на моём нынешнем проекте используется Fortify), тестирование производительности (Lighthouse), тестирование на CSS регрессию (Hermione).

Сфера тестирования, как и сфера программирования, очень широкая. Здесь нет готового алгоритма, и многие технологии и решения зависят от инструментов, на которых мы проводим тестирование. Мы используем unit-тестирование, end-to-end-тестирование, интеграционное тестирование. Интеграционные и unit-тесты, как правило, проводятся разработчиками. End-to-end-тестирование — тестировщиками, которые пишут автоматические тесты.

Если говорить про тесты, которыми занимаются разработчики, всё зависит от фреймворков (Angular, React и т. д.). Для Angular многие компании используют инструменты Karma и Protractor, для React — Jest и Enzyme. Интеграционное тестирование делается на этих же вещах.

Основным инструментов для end-to-end-тестов, как правило, является Selenium. Далее идёт обёртка высокого уровня, более доступная пользователям. Это может быть Puppeteer, Cypress, Webdrive.io и прочие.

В нашей компании end-to-end-тесты делают либо на C#, либо на Java. На C# у нас свои тестовые решения, которые мы написали и реализовали. На Java мы используем фреймворк Selenide, который легко все масштабирует и обладает рядом полезных функций, которые работают «из коробки». Например, удобная интеграция с Allure, который мы используем для сбора и анализа результатов тестов. На самом деле, нет большой разницы, на чем писать автотесты, это можно делать на любом языке, поэтому смотрите, что вам ближе. Фреймворки типа Puppeteer и Webdriver.io подходят для людей, которые умеют писать код. Фреймворки типа Cypress имеют UI для тестеров и более дружелюбны. Но для использования большинства фреймворков все же нужно изучить язык программирования. После того, как вы выбрали фреймворк, обязательно нужно прочитать документацию. На Medium есть много полезных статей на эту тему. Также на Software Testing есть неплохие курсы для тестирования с нуля и по C#, и по Java. У нас в компании также есть платная подписка на Pluralsight. Можно подобрать что-то на Udemy, Lynda, Coursera. Будет полезна книга «Шаблоны тестирования xUnit: рефакторинг кода тестов», где неплохо описаны антипаттерны, которые есть в тестах.

Еще при интеграционном тестировании помогает работа со скриншотами экрана. Мы делаем эталонный снимок экрана и сравниваем с ним все последующие. Это подходит для устоявшихся продуктов, где UI редко меняется. Есть сервисы, которые позволяют делать сравнение в разных браузерах. BrowserStack – отличный инструмент как для мануального, так и для автоматического тестирования. Плагины для тестирования, например, Wave, помогают быстро и качественно протестировать, насколько удобен и читабелен продукт.

Для начала я бы рекомендовал отличный инструмент «проверь себя». Часто случается, что разработчики передают задачу тестировщикам, не проверив перед этим свою работу, что приводит к потерям времени: сперва задача находится в очереди на тестирование, потом тестировщик находит банальную ошибку в первом же очевидном тест-кейсе, после этого задача снова ждёт, пока разработчик переключится с нового задания на старое, а дальше цикл повторяется, пока не будут устранены ошибки «на поверхности». Только после этого этапа тестировщик сможет полностью погрузиться в задачу и выполнить полноценное тестирование. Поэтому, в первую очередь, надо «покликать» кнопочки, заполнить формы, пройти хотя бы планируемый, «идеальный» сценарий.

На следующем этапе можно перейти на более сложный уровень — встать на место пользователя и попытаться выполнить его задачи. В таком случае важно абстрагироваться от собственной разработки и пойти не по «счастливому сценарию», а смоделировать более вероятную и близкую к реальности ситуацию, ведь пользователи не догадываются, через какой именно путь вы хотите их провести. Можно даже попытаться сломать свою систему, как если бы это был чужой проект: заполнить форму для телефона буквами, поля email — цифрами, внезапно закрыть окно подтверждения действия и так далее. Это займёт совсем немного времени разработчика, зато позволит передать в тестирование более качественную работу и доставить её в «прод» намного быстрее.

Следующий уровень — проверка в разных средах. При разработке под веб стоит проверить свою работу во всех браузерах (Chrome, IE, Safari и др.). Также хорошим испытанием будет тестирование на различных разрешениях экрана: идеально — на разных устройствах, не идеально — встроенными в браузеры средствами. Фронтенд-приложения для мобильных устройств тоже надо проверять на разных устройствах, но подойдут и эмуляторы, например Genymotion или Bluestacks. Для проверки кросс-браузерности и кросс-платформенности можно воспользоваться browserstack.com.

Безусловно, существуют инструменты автоматизированного тестирования. Для этого есть множество разработок под различные виды фронтенда (веб, мобильный, десктоп). Например Selenium IDE работает как надстройка к браузеру, умеет заполнять поля, нажимать кнопочки и так далее. Если проект уже устоявшийся и изменений на фронте нет либо мало, верстку допустимо тестировать при помощи сравнений скринов с эталоном, инструментов для этого тоже много. Для более сложных проверок можно написать под Selenium пару скриптов на Python, тогда автоматически выполняется валидация полей, проверяется наличие кнопок, текстов, ссылок и тому подобное.

Для мобильного фронтенда используется Katalon, он позволяет сделать запись своих действий, а потом повторять этот сценарий. Такой подход подойдёт для проверки поломок после внесённых во фронт изменений, так называемое регресс-тестирование.

Итак, что и для каких тестов используют при тестировании фронтенда?

Напоминаем, что вы можете задать свой вопрос экспертам, а мы соберём на него ответы, если он окажется интересным. Вопросы, которые уже задавались, можно найти в списке выпусков рубрики. Если вы хотите присоединиться к числу экспертов и прислать ответ от вашей компании или лично от вас, то пишите на experts@tproger.ru, мы расскажем, как это сделать.

Следите за новыми постами
Следите за новыми постами по любимым темам
13К открытий13К показов