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

Вначале несколько слов о Cypress

Cypress — это инструмент сквозного тестирования на основе JavaScript, разработанный для современной автоматизации веб-тестирования. Cypress стал популярным инструментом сквозного тестирования веб-приложений благодаря своим сравнительно мощным функциям, удобному интерфейсу и быстроте выполнения тестов.

Существует множество pros and cons относительно преимуществ наиболее популярных на сегодняшний день фреймворков для тестирования веб-приложений (Cypress, Playwright, Selenium, Puppeteer и т.д.), однако я отдаю свое предпочтение Cypress, которым пользуюсь повседневно на протяжении уже нескольких лет.

Что дает использование Docker в тестировании веб-приложений с помощью Cypress

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

Docker — это сервис с открытым исходным кодом для упаковки, доставки и запуска приложений в изолированной контейнерной среде.

Как известно, без Docker сейчас фактически не обходится разработка ни одного веб-приложения. Так, использование технологий контейнеризации Docker дает ряд серьезных преимуществ для тестирования веб-приложений, таких как обеспечение единообразия среды для запуска тестов; независимость от параметров и настроек системы, на которой осуществляется запуск тестов; надежность, воспроизводимость и предсказуемость результатов тестов при их запуске в различных окружениях и т.д.

В частности, применение Docker в Cypress тестировании может быть полезным поскольку:

  1. Обеспечивает запуск автотестов Cypress в изолированной тестовой среде, поскольку контейнер, в котором запускаются тесты, изолирован от других контейнеров и от внешней среды. В таком случае тесты по сути не зависят от того, что находится вне контейнера, что обеспечивает надежность и бесперебойность в работе тестов при каждом новом запуске.

    Так, в случае развертывания контейнеров с тестами локально это означает, что отсутствие на хост-компьютере Node.js, Cypress, какого-либо браузера или их определенных версий не станет препятствием для запуска автотестов Cypress. 

  2. Позволяет запускать приложения и автотесты Cypress как локально на разных хост-компьютерах, так и развертывать их в конвейерах CI/CD и облачных сервисах за счет обеспечения  единообразия и согласованности тестовой среды. При переносе Docker образа с одного сервера на другой контейнеры с самим приложением и тестами будут работать одинаково независимо от используемой операционной системы, наличия Node.js, Cypress, браузеров и т.д. Это гарантирует воспроизводимость автотестов Cypress и предсказуемость результатов их запуска для разных базовых систем. 

  3. Освобождает от необходимости устанавливать какие-либо зависимости вне контейнеров — на самих серверах, на которых запускаются контейнеры. Docker позволяет быстро развернуть необходимое окружение для запуска автотестов Cypress, в связи с чем не нужно каждый раз устанавливать зависимости операционной системы, необходимые браузеры и тестовые фреймворки.

  4. Существенно упрощает интеграцию автотестов Cypress в процесс разработки веб-приложений, встраивание и развертывание тестов в различных конвейерах CI/CD, облачных сервисах и т.д., освобождая от необходимости  устранения проблем в совместимости при установке, донастройки и т.д. К тому же, Docker позволяет безопасно перемещать тесты между окружениями развертывания ПО, например с ноутбука тестировщика в stage или production окружения.

  5. Ускоряет процесс тестирования за счет сокращения общего времени на тестовые запуски. Это достигается за счет масштабирования, т.е. увеличения количества контейнеров, запуска автотестов Cypress в разных контейнерах в параллельном режиме, возможностей параллельного кросс-браузерного тестирования с использованием Docker Compose и т.д.

Коротко об официальных образах Cypress

На сегодня в общедоступном хранилище образов Docker Hub, а также в соответствующем репозитории cypress-docker-images на GitHub размещены 4 официальных Docker образа Cypress:

Docker репозитории этих образов включают в себя их различные версии и теги. Количество скачиваний каждого образа (за исключением cypress/factory) на момент написания статьи составляло более 50 млн.

Теперь давайте разберемся, как можно взаимодействовать с каждым из указанных образов для запуска автотестов Cypress в создаваемых на их основе контейнерах. При этом стоит отметить, что зачастую данные контейнеры генерируются не напрямую из четырех указанных образов, а на основе настраиваемых "кастомных" образов, которые в свою очередь собираются из официальных образов. Это возможно благодаря тому, что Docker позволяет переиспользовать официальные образы в качестве базового слоя и собирать на их основе настраиваемые образы путем добавления необходимых дополнительных слоев.

Краткое отступление о тестовом проекте

Для демонстрационных целей в настоящей статье я буду использовать имеющийся у меня простейший проект Cypress-Docker с автотестами Cypress для тестирования моего блога Testing with Cypress на Medium. 

В проекте уже установлены некоторые зависимости — Cypress 12.12.0 и Typescript 5.0.4:

а также имеется один spec.cy.ts файл с тестовым набором из трех тривиальных тестов для главной страницы блога:

Запустив данные тесты в headless режиме, убеждаемся в успешном выполнении тестов:

Здесь стоит обратить внимание на параметры среды при запуске тестов локально на компьютере — Node.js 19.8.1, Cypress 12.12.0 и предустановленный в нем браузер Electron 106. В дальнейшем при запуске тестов внутри Docker контейнеров эти параметры будут варьироваться в зависимости от наполнения используемых Docker образов. Также отмечу, что локально уже установлена программа Docker Desktop.

Запуск тестов в Docker контейнерах 

Теперь давайте рассмотрим, как можно запустить имеющиеся тесты внутри Docker контейнеров, используя каждый из официальных Docker образов Cypress.

CYPRESS/INCLUDED

Я бы хотел начать именно с этого образа, поскольку он в отличие от остальных включает в себя все зависимости операционной системы, предустановленный Cypress и некоторые браузеры. За счет этого cypress/included доступен для использования что называется в ”чистом” виде без необходимости добавления дополнительных слоев. Образ позволяет создавать контейнеры и запускать в них автотесты Cypress, используя всего лишь одну команду.

Для скачивания образа из хранилища образов Docker Hub достаточно выполнить в терминале следующую команду:

> docker pull cypress/included:12.12.0

Здесь важно отметить, что рекомендуется всегда указывать определенный тег образа, а не полагаться на тег по умолчанию (latest), поскольку latest версия по мере выпуска новых версий образа постоянно меняется. Стоит добавить, что в случае cypress/included тег образа соответствует версии предустановленного в нем Cypress, например в моем случае это 12.12.0 (stable). Таким образом, если не указывать версию образа, то есть вероятность, что тесты будут запущены в разных версиях Cypress, поскольку контейнеры для запуска тестов будут сгенерированы из образов с различными тегами.

Наличие указанного образа после завершения его загрузки можно проверить с помощью простой команды:

> docker images

Скачанный cypress/included образ указан первым в списке загруженных образов:

Чтобы  узнать, что включает в себя данный образ достаточно запустить следующую команду:

> docker run -it --entrypoint=cypress cypress/included:12.12.0 info

В результате получаем информацию о предустановленной в образе версии Node.js, Cypress, трех браузерах, а также других характеристиках:

Для создания контейнера из скачанного образа и запуска в нем имеющихся автотестов Cypress выполним следующую команду:

> docker run -it -v $PWD:/e2e -w /e2e cypress/included:12.12.0

Давайте разберем структуру данной команды:

  • docker run  означает создание и запуск контейнера на основе указанного образа

  • -it — интерактивный терминал

  • -v $PWD:/e2e — замена содержимого директории внутри контейнера на содержимое текущей директории проекта, где $PWD — абсолютный путь к текущей директории (корневой папке проекта),  /e2e — путь к директории внутри контейнера

  • -w /e2e — определение рабочей директории внутри контейнера, где /e2e — путь к указанной директории

  • cypress/included:12.12.0 — указание на образ, на основе которого будет создан контейнер.

В ходе исполнения данной команды будет создан Docker контейнер, в котором Cypress запустит тесты на встроенном браузере Electron. Важно отметить, что образ cypress/included включает в себя инструкцию ENTRYPOINT ["cypress" "run"], что обеспечивает автоматический запуск Cypress внутри созданного на основе данного образа контейнера.

При желании возможно дополнить команду флагом --name для указания определенного имени создаваемому контейнеру, а также флагом --rm для автоматического удаления контейнера после его остановки:

> docker run -it -v $PWD:/e2e -w /e2e  --name cypress-tests --rm cypress/included:12.12.0

При этом для управления поведением Cypress можно указывать дополнительные параметры по аналогии с тем, как мы запускаем Cypress в headless режиме без использования Docker. Так, можно указать конкретный браузер или spec файл для запуска, настроить параметры записи, параллельного запуска тестов и т.д. Например, чтобы запустить тесты в любом из предустановленных в образе браузеров (Chrome, Edge, Firefox) достаточно дополнить команду флагом CLI -b с указанием названия браузера: 

> docker run -it -v $PWD:/e2e -w /e2e cypress/included:12.12.0 -b chrome

Или если предположить, что в изначально в проекте имеется несколько spec-файлов, то для запуска определенного файла/файлов следует дополнить команду запуска флагом -s с указанием абсолютного пути к указанному файлу/файлам:

> docker run -it -v $PWD:/e2e -w /e2e cypress/included:12.12.0 -s cypress/e2e/spec.cy.ts

Давайте создадим контейнер и запустим имеющиеся тесты внутри него с использованием браузера Chrome, используя вышеуказанную команду запуска:

Видим, что указанные параметры среды запуска тестов (Node.js 18.16.0, Cypress 12.12.0, Chrome 113) соответствуют характеристикам, полученным при запросе информации о содержимом скачанного образа cypress/included, и при этом отличны от параметров при первоначальном запуске тестов локально. Таким образом, для запуска тестов потребовалась всего лишь одна команда.

Многократный запуск тестов в одном контейнере

Важно отметить, при каждом очередном запуске автотестов Cypress на основе использованной команды docker run … будет создан и запущен новый контейнер. Чтобы избежать этого и иметь возможность многократного запуска тестов внутри одного контейнера нам необходимо подключиться к процессу внутри контейнера, чтобы контролировать запуск тестов из контейнера. Для этого достаточно доплнить команду флагом --entrypoint с параметром /bin/bash

> docker run -it -v $PWD:/e2e -w /e2e --entrypoint=/bin/bash cypress/included:12.12.0

В результате у нас появляется возможность управлять повторным запуском тестов из контейнера с помощью команды cypress run или, например, просмотреть файлы и папки внутри контейнера, используя стандартную команду ls -la.

Важно отметить, что кроме вышеуказанного способа запуска тестов с использованием образа cypress/included мы может собрать свой собственный образ на его основе с помощью Dockerfile. Однако в случае cypress/included в большинстве случаев это не имеет значимого практического смысла (чего не скажешь о других образах), поскольку данный образ уже включает в себя все необходимые параметры тестовой среды. Более того, как было указано ранее, cypress/included уже содержит инструкцию ENTRYPOINT ["cypress" "run"], в связи с чем нет необходимости явно определять команду по запуску автотестов Cypress в Dockerfile.

CYPRESS/BROWSERS

Данный образ включает в себя все зависимости операционной системы и некоторые браузеры. При этом он не содержит предустановленного Cypress, чем схож с cypress/factory, основное различие состоит в том, что в последнем можно по желанию настроить определенные версии браузеров и зависимостей операционной системы, а также предустановить Cypress нужной версии.  

Каждому образу cypress/browsers присваивается имя по следующей общей схеме:

cypress/browsers:node-<full Node version>-chrome-<Chrome version>-ff-<Firefox version>-edge-<Edge version>

При этом в некоторых версиях образа может быть только один или два браузера в различных комбинациях Chrome — Firefox — Edge.

Давайте создадим образ на основе cypress/browsers. При этом я буду намеренно использовать предпоследнюю версию образа, чтобы продемонстрировать различие в используемой версии браузера. В данном случае Dockerfile для сборки настраиваемого образа будет иметь следующий вид:

Вначале в инструкции FROM определен базовый образ, все зависимости и конфигурации которого будут включены в создаваемый образ. В частности, это Node.js 18.16.0, а также предустановленные браузеры — Chrome 112.0.5615.121-1, Firefox 112.0.1 и Edge 112.0.1722.48-1.

На следующем шаге WORKDIR создается рабочая директория /tests, в которой будут выполняться все последующие команды. Если такая директория не существует, данная инструкция создаст ее.

Далее указанные в команде COPY файлы и папки будут скопированы из текущей директории проекта (в которой находится Dockerfile) в рабочую директорию внутри образа. Стоит отметить, что я не копирую все содержимое проекта, поскольку очевидно в этом нет необходимости. В частности, нет смысла в копировании директории node-modules с уже установленными зависимостями, поскольку последние зависят от параметров тестовой среды. Также, чтобы избежать копирования, можно создать .dockerignore файл, явно указав в нем node-modules.

В инструкции RUN задается команда, в соответствии с которой в рабочей директории устанавливаются все необходимые зависимости, и в частности на этом этапе будет установлен Cypress.

На последнем шаге в ENTRYPOINT указана команда по запуску Cypress, которая будет выполняться внутри генерируемых из данного образа контейнеров. Стоит отметить, что инструкция записана в exec форме.

Давайте инициируем сборку образа на основе вышеописанных инструкций, выполнив команду:

> docker build -t my-cypress-browsers:1.0.0 .

где docker build — команда по созданию настраиваемого образа

-t my-cypress-browsers:1.0.0 — добавление произвольного имени и тега для создаваемого образа

. означает текущую директорию, в которой размещен Dockerfile, на основе которого создается образ.

Как видим успешная сборка образа завершилась сообщением FINISHED:

Проверим наличие созданного образа, выполнив команду:

> docker images

Видим что собранный образ указан первым в списке имеющихся образов:

Далее из созданного образа сгенерируем контейнер и запустим в нем имеющиеся автотесты Cypress в браузере Chrome с помощью команды:

> docker run -it my-cypress-browsers:1.0.0 -b chrome

Как видим тесты автоматически запускаются в среде с указанными в базовом образе версиями Node.js и Chrome:

CYPRESS/BASE

Данный образ включает все зависимости операционной системы, необходимые для запуска Cypress, но при этом не содержит самого Cypress и предустановленных браузеров. Как указали разработчики в описании образа, тег конкретного образа соответствует версии Node.js или операционной системы, на которой он собран.

Данный образ привлекателен тем, что он значительно (почти в 2 раза) меньше по размеру образов cypress/included и cypress/browsers, что выглядит весьма привлекательно в случае, если нет необходимости использовать отдельные браузеры.

Чтобы собрать наш собственный образ на основе cypress/base создадим новый Dockerfile со следующими инструкциями:

В инструкции FROM в качестве базового слоя определен cypress/base:18.15.0, где тег 18.15.0 сoответствует версии предустановленной в образе Node.js. При этом, как и в предыдущем случае, я указываю предпоследнюю версию базового образа, чтобы продемонстрировать различие в используемых версиях Node.js.

На следующем шаге, как и ранее, создается рабочая директория для выполнения всех последующих команд. Инструкция COPY содержит указание на файлы и папки, подлежащие копированию в рабочую директорию внутри образа. Командой RUN обеспечивается установка необходимых зависимостей в рабочей директории. На завершающем этапе указана команда для запуска тестов внутри генерируемых из данного образа контейнеров.  

Зададим имя и тег образа — my-cypress-base:1.0.0 и инициируем его сборку уже известной командой: 

> docker build -t my-cypress-base:1.0.0 .

Успешная сборка образа завершилась сообщением FINISHED:

Как и ранее, проверим наличие собранного образа используя команду:

> docker images

Видим, что образ my-cypress-base указан первым в списке имеющихся образов:

Как и указывалось ранее, из-за отсутствия браузеров созданный образ значительно (более, чем в 2 раза) меньше по размеру образов, собранных на основе cypress-included и cypress-browsers.

Далее сгенерируем контейнер на основе созданного образа и запустим в нем автотесты Cypress используя уже известную команду:

> docker run -it my-cypress-base:1.0.0

Убеждаемся, что тесты успешно запускаются в предустановленном браузере Electron 106. При этом, как и предполагалось, указанная версия Node.js — 18.15.0 соответствует тегу базового образа cypress-base и отличается от ранее использованной версии (18.16.0):

Важно заметить, что если мы попробуем запустить тесты в другом браузере, например дополнив соответствующую команду флагом -b chrome, то несмотря на то, что соответствующий контейнер будет сгенерирован, очевидно, что запуск тестов не состоится в связи с отсутствием браузера Сhrome:

Как указано в тексте ошибки, единственным доступным браузером является Electron, что очевидно поскольку базовый образ cypress/base не содержит предустановленных браузеров.

Далее рассмотрим последний из официальных образов — cypress/factory.

CYPRESS/FACTORY

Данный образ появился относительно недавно, первая версия образа с тегом 1.0.0 была загружена на Docker Hub около 4 мес. назад. Cypress/factory позволяет собирать настраиваемые образы с определенными версиями:

  • node

  • yarn

  • chrome

  • firefox

  • edge

  • cypress

Как указано в инструкции к данному образу использование cypress/factory предоставляет следующие преимущества:

  • свободу в выборе версий для тестирования

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

  • обеспечивает создание более компактных по размеру образов с учетом возможности не включать в сборку неиспользуемые браузеры

  • возможность тестирования нескольких версий конкретного браузера

  • упрощает обслуживание cypress-docker репозитория

  • использование большего количества вариаций контейнеров с наименьшими издержками.

Действительно, одним из основных преимуществ cypress/factory является возможность "кастомной" настройки содержимого образа, которая позволяет в случае необходимости значительно уменьшить его размер.

Необходимые версии Node.js, Cypress, а также браузеров могут быть переданы в качестве аргументов ARG при сборке образа. Предположим, я планирую собрать образ, включив в него наиболее актуальные на сегодняшний день версии Node.js, а также браузеров Chrome, Edge, Firefox:

  • Node.js 20.1.0

  • Chrome 113.0.5672.92-1

  • Edge 113.0.1774.42-1

  • Firefox 113.0

В таком случае мне необходимо передать указанные параметры в качестве аргументов для сборки образа. При этом есть разные способы их передачи:

  • в виде инструкций в Dockerfile

  • внутри команды запуска сборки образа с флагом --build-arg

  • используя Docker Compose, указав их в файле docker-compose.yml

Если при создании образа вообще не передать никаких аргументов, то по умолчанию созданный образ будет содержать лишь Node.js.

Давайте сначала рассмотрим вариант указания аргументов ARG в Dockerfile. Для этого создадим в корневой папке проекта Dockerfile со следующим содержимым:

Как видно вначале еще до инструкции FROM определены аргументы ARG для сборки образа, в которых указаны требуемые версии Node и браузеров. При этом, как отмечено в описании cypress/factory, необходимо использовать точные версии, подстановочные знаки или сокращения не поддерживаются.

Далее в инструкции FROM указано, что базовым образом для создания настраиваемого образа является образ cypress/factory. На следующем шаге в WORKDIR определена рабочая директория /tests внутри образа для выполнения всех последующих команд.

В инструкции COPY указаны файлы и папки из текущей папки проекта (в которой находится Dockerfile), которые будут скопированы в WORKDIR.

Далее в инструкции RUN задается команда, которая будет запущена в ходе сборки образа. В данном случае это позволит установить необходимые зависимости, указанные в файле package.json. Стоит отметить, что запуск данной команды на этом этапе создания образа, по сути снимает необходимость копирования всего проекта целиком при выполнении инструкции COPY.

На последнем этапе в инструкции ENTRYPOINT указана команда для запуска Cypress внутри контейнеров, которые будут сгенерированы на основе данного образа.

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

> docker build -t my-cypress-factory:1.0.0 . 

После завершения процесса сборки образа проверим его наличие запустив уже известную команду:

> docker images

Как видим созданный образ отображается первым в списке существующих образов:

Инициируем создание контейнера на основе созданного образа и запуск автотестов Cypress в нем, запустив команду:

> docker run -it my-cypress-factory:1.0.0 -b chrome

Как видно, в созданном контейнере тесты автоматически запускаются в среде с указанными в Dockerfile версиями Node.js и браузера Chrome:

Очевидно, что для управления поведением Cypress в ходе выполнения тестов мы также можем передать в команду запуска контейнера дополнительные параметры, используя флаги CLI. Так, в вышеуказанной команде тесты запускаются в браузере Chrome с помощью флага -b

Чтобы собрать настраиваемый образ без добавления аргументов ARG в Dockerfile, достаточно запустить в терминале следующую команду:

> docker build --build-arg NODE_VERSION='20.1.0' --build-arg CHROME_VERSION='113.0.5672.92-1' --build-arg EDGE_VERSION='113.0.1774.42-1' --build-arg FIREFOX_VERSION='113.0' -t my-cypress-factory:1.0.0 .

При этом данное действие переопределяет значения ARG в случае, если они были также заданы в Dockerfile.

Несмотря на очевидную гибкость при сборкe настраиваемых образов на основе cypress/factory, возможно случаи комбинирования внутри образа несовместимых друг с другом версий. Например, определенные версии Cypress могут не поддерживать некоторые версии Node.js. Очевидно, это позволит собрать образ, однако сгенерированные на его основе контейнеры не будут работать корректно. Как утверждают сами разработчики, как сам образ, так и созданные на его основе контейнеры, подходят лишь для тестового использования и не предназначены для использования в production среде. 

Заключительные замечания

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

Надеюсь, что данная статья была полезна Вам для понимания основ использования контейнеризации в тестировании Cypress. Стоит отметить, что за пределами изложения остались случаи запуска автотестов Cypress в составе нескольких контейнеров, параллельного и кросс-браузерного тестирования, что безусловно заслуживает написания отдельной статьи.

Использованные в статье Dockerfiles, а также сам тестовый проект, вы можете найти в соответствующем репозитории моего блога на GitHub.

Спасибо за внимание и удачного тестирования!

Комментарии (9)


  1. evg_gusev
    12.05.2023 22:20
    +1

    полезная статья, благодарю за подробное изложение


    1. alex_sanzh Автор
      12.05.2023 22:20
      +1

      благодарю за комментарий


  1. alekseeva_qa
    12.05.2023 22:20
    +2

    спасибо, на рабочем проекте используем Base в конвейере CircleCI. Было б интересно также детально про настройку docker-compose.yml


    1. alex_sanzh Автор
      12.05.2023 22:20
      +1

      благодарю за комментарий


  1. Avrast12345
    12.05.2023 22:20
    +1

    Интересная информация, выделил для себя много нового.


    1. alex_sanzh Автор
      12.05.2023 22:20

      благодарю за комментарий


  1. francyfox
    12.05.2023 22:20
    +1

    часто в постах фигурирует нативный докер, хотя им неудобно пользоваться. Кому нравится писать километровые команды с кучей аргументов? Кстати ключевая фишка это хранение артефактов, я использовал cypress на gitlab ci и очень быстро перегрузил хранилище тонной мусора, потом узнал что можно настроить авточистку. Был ещё парадокс, после подключение к багтрекингу (кажется sentry) артефакты были доступны всегда, как будто они сохранялись на сервере sentry


    1. alex_sanzh Автор
      12.05.2023 22:20

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


  1. alex_sanzh Автор
    12.05.2023 22:20

    .