Любите ли вы тесты, как люблю их я: всеми фибрами души, со всей страстью и энтузиазмом, на которые только способен разработчик, жадный до полного покрытия кода? В этой статье я расскажу о тестировании кода с помощью Puppeteer — сервиса, который позволяет проверять работу скриптов в их естественной среде обитания — в браузере. Это не полноценный туториал по Puppeteer, а скорее набор советов о том, как писать осмысленные и стабильно работающие тесты. Но сначала немного о том, для чего использую Puppeteer я. Я работаю в команде, которая занимается разработкой трекинговых решений, и неотъемлемой частью моей работы является покрытие новых функций тестами. Тесты бывают разные: от простых unit-тестов до масштабного интеграционного тестирования. От вида тестов зависит и окружение, в котором они будут запускаться. Каждый раз, когда я пишу тесты, они помогают обнаружить баги. Но у тестирования есть и другие плюсы. Один из них — обнаружение подводных камней. Подготовка тест-кейсов позволяет сложить в голове целостную картину бизнес-логики и того, как она должна работать. Тесты заставляют еще раз всё обдумать, и, быть может, это-то и спасет вас от неожиданных проблем на проде. Кроме того, хорошие тесты — это вторая документация (а если ее нет, то единственная). По ним можно понять, какое поведение ожидается от кода, а не как оно достигнуто. Поэтому не стоит усложнять тесты и нагромождать их utils-функциями (а тем более писать тесты на тесты). И конечно же, не стоит забывать, что тесты — это весело! Для их написания используется множество библиотек, и у каждой из них своя специфика, так что в процессе подготовки тестов возникает не меньше интересных задач. Так как это не туториал, а набор советов по решению проблем, с которыми я столкнулся, здесь не будет информации, как установить нужные библиотеки и начать работу. С этим вам поможет справиться репозиторий Puppeteer и поисковая строка этого интернет-ресурса. Весь код, связанный с этой статьей, доступен в репозитории puppeteer-showcase. В ходе работы скриптов и тестов могут появляться побочные эффекты: записываться cookie, добавляться записи в localStorage и т. д., поэтому каждый тест надо запускать с чистого листа — в новом окне браузера. Это достаточно очевидный, но очень важный момент: все написанные тесты должны быть независимыми и работать без опоры друг на друга. Писать связанные тесты — плохо! Предположим, у нас есть кнопка (#button1), при нажатии на которую выполняется следующий код: Этот скрипт направляет пользователя на URL, полученный от сервера. В случае ошибки скрипт логирует ее и направляет пользователя на URL по умолчанию. В голову приходит несколько сценариев для тестирования: Опишем их: Кода получилось много, как это часто бывает с тестами. Посмотрим внимательно на первый из них: Пайплайн такой: Постойте, а что, если скрипт не успеет совершить запрос и перенаправить пользователя за 100 мс? Может быть, увеличить таймаут до 1 с? А что, если и за секунду не успеет? Да и если каждый тест будет ждать по секунде, сколько же будут работать все тесты? Ответ прост: не используйте таймауты. У Puppeteer API есть множество методов, которые позволяют избежать таймаутов, дождавшись вместо этого совершения какого-либо действия. В нашем случае подойдет waitForNavigation. Этот метод ожидает, пока не произойдет смена страницы. Хороший тест будет выглядеть так: И никаких таймаутов! Это очень простой пример, но от того не менее показательный. Для того чтобы выйти победителем из битвы с таймаутами, необходимо продумывать очередность await-ов: сначала действие, затем его ожидание. Тогда и тесты ускорятся, и CI не будет падать, когда не хватит таймаута. Есть две независимые среды выполнения JavaScript-кода при написании тестов с Puppeteer: Эти две среды изолированы, у них нет общей области видимости. Однако при помощи API Puppeteer может «общаться» с браузером. Также с браузером взаимодействуют и тесты, в том числе выполняют код и получают его результат через evaluate. Понимание того, что бизнес-логика и тесты для нее исполняются в двух разных изолированных средах, между которыми есть только один мостик в виде Puppeteer, является критичным.  Лучше картинки с котиком это может продемонстрировать только тест. На этот раз бизнес-логика будет выглядеть чуть проще: и будет запускаться при нажатии на кнопку #button2. Тогда напишем два теста: А вот и тесты: Пайплайн тут следующий: Запускаем тесты и видим заветную картину:  should not print message to node console on button2 click и should print message to browser console on button2 click прошли, значит, я никого не обманул. Еще одна прелесть тестов с использованием Puppeteer — их наглядность. Помните, как мы при запуске браузера указывали параметры? Мы использовали headless: true потому, что тесты так проходят (или падают) быстрее, так как не нужно тратить ресурсы на запуск графической оболочки браузера. Однако если указать следующие параметры: и запустить тесты, мы сможем понаблюдать за тем, что делает Puppeteer по нашим указаниям:  В этой статье я поделился небольшой частью интересностей Puppeteer, оставив за кадром его преимущества вроде кроссбраузерности (с некоторыми оговорками) и непростые задачи, которые приходилось решать для повышения стабильности тестов. Если мое повествование нашло отклик в вашей душе, напишите об этом в комментариях.  Еще не могу не посоветовать статью моего коллеги о том, какой путь он проделал для того, чтобы попасть на стажировку и успешно ее пройти. Если вы устали от разноцветного кода, советую ее прочесть: там есть смешные картинки.  Спасибо за внимание!  исходный код  Backend developer