Перевод статьи «Cucumber in Cypress: A step by step guide».
Один из самых распространенных вопросов, которые встречаются на вебинарах и прямых трансляциях, звучит так: Как использовать “X” в Cucumber? . Будь то тестирование API, cy.session()
или другая функциональность, Cucumber является обязательным требованием во многих командах.
Основным преимуществом использования Cucumber является возможность использования синтаксиса Gherkin для определения тестов. Все тесты пишутся как сценарии поведения, поэтому тесты не только выполняют роль проверки функциональности, но и служат своего рода документацией. Цель такого подхода – обеспечить большую наглядность того, что тестируется. Преимущество заключается в том, что, помимо инженеров, другие заинтересованные лица компании могут проверить, выполнены ли критерии приемки.
Особенно хорошо такой подход работает в медицине и банковском секторе, где тесты могут быть не только функциональными, но и использоваться для создания документации или отчетов высокого уровня.
Содержание:
- Использование Cucumber
- Установка
- Тестовые сценарии и шаги
- Добавление параметров в определения шагов
- Тестирование на основе данных
- Работа с массивом данных
- Группировка тестов
- Использование хуков
- Тегирование тестов
- Конфигурация
- Отчетность
- Заключение
Подпишитесь на наш ТЕЛЕГРАМ КАНАЛ ПО АВТОМАТИЗАЦИИ ТЕСТИРОВАНИЯ
Использование Cucumber
Нередко встречается критика синтаксического подхода Gherkin. Основное возражение против него заключается в том, что он использует тестирование методом “черного ящика”. Далеко не все считают такой подход эффективным – особенно в случае с Cypress.
Тесты Cypress выполняются внутри браузера, что дает вам возможность входить во внутренние компоненты приложения, получать доступ к API, кэшировать сеансы, изменять состояние приложения, имитировать сеть. Хорошо разработанные тесты Cypress могут помочь вам добиться существенного покрытия с помощью небольшого количества тестов.
При использовании метода “черного ящика” отбрасываются все возможности Cypress, и он используется просто как средство автоматизации тестирования.
Кроме того, возникает вопрос обслуживания и читаемости. Команды Cypress хорошо читаются из коробки, и, применяя некоторые хорошие практики, можно сохранить свои тесты простыми и легкими в обслуживании даже в приложениях, которые регулярно меняют свое поведение.
В Cucumber используются определения, основанные на шагах, которые заключают каждую серию команд в отдельный файл. Хотя они и отличаются хорошей читаемостью, любое изменение, вносимое в приложение, может потребовать переопределения или добавления нескольких шагов. Это означает, что чем больше система, тем сложнее вводить новые изменения.
Это руководство создано для того, чтобы вы могли эффективно настроить Cypress с Cucumber, если это потребуется в вашей компании.
Установка
Для начала необходимо установить плагин cypress-cucumber-preprocessor. В настоящее время существует несколько различных версий, но эта – самая популярная, и она активно поддерживается. Установить его можно, выполнив следующую команду:
npm i @badeball/cypress-cucumber-preprocessor
Помимо установки препроцессора, в документации к плагинам рекомендуют установить бандлер esbuild, который значительно ускорит работу.
npm i @bahmutov/cypress-esbuild-preprocessor
После установки этих пакетов необходимо настроить Cypress на использование плагинов. Окончательная конфигурация будет выглядеть примерно так:
import { defineConfig } from "cypress"; import createBundler from "@bahmutov/cypress-esbuild-preprocessor"; import { addCucumberPreprocessorPlugin } from "@badeball/cypress-cucumber-preprocessor"; import createEsbuildPlugin from "@badeball/cypress-cucumber-preprocessor/esbuild"; export default defineConfig({ e2e: { specPattern: "**/*.feature", async setupNodeEvents( on: Cypress.PluginEvents, config: Cypress.PluginConfigOptions ): Promise<Cypress.PluginConfigOptions> { await addCucumberPreprocessorPlugin(on, config); on( "file:preprocessor", createBundler({ plugins: [createEsbuildPlugin(config)], }) ); return config; }, }, });
Здесь есть, что распаковывать, поэтому рассмотрим все пошагово.
Конфигурационный файл написан на языке TypeScript. Файл на Javascript может быть немного проще, но по сути содержит все те же части. Мы импортируем различные пакеты и добавляем их в нашу функцию setupNodeEvents()
.
Атрибут specPattern
сообщает Cypress, что мы хотим искать файлы .feature
в нашей папке e2e
. Это означает, что он будет игнорировать все другие форматы и использовать в качестве теста только эти файлы.
Функция addCucumberPreprocessorPlugin()
займется обработкой файлов .feature
и преобразованием их в Javascript. Поскольку Cypress работает в браузере, нам необходимо убедиться, что все, что мы запускаем (будь то файлы.ts
, .jsx
или другие форматы), в конечном итоге компилируется в обычный Javascript. Именно это и делают препроцессоры.
В части on("file:preprocessor")
происходит объединение плагина esbuild с плагином cucumber, чтобы они хорошо работали вместе.
Заключительное утверждение return config
позволяет убедиться в том, что все, что мы настроили, действительно будет установлено в нашей конфигурации. Об этом шаге часто забывают, поэтому, если ваши плагины ведут себя так, как будто они вообще не установлены, проверьте наличие этого оператора возврата.
Поскольку компиляция в Javascript является важной частью работы с файлами
.feature
, обычно начальная настройка является самым большим препятствием, которое необходимо преодолеть. Существует наиболее простая для работы настройка из документации, но если вы работаете с другим бандлером, например Webpack или Browserify, вы можете найти примеры здесь.
Теперь, когда плагин установлен и настроен, давайте рассмотрим, как писать тесты.
Сценарии и этапы тестирования
Начнем с написания простого тестового сценария в синтаксисе Gherkin. Создайте новый файл cypress/e2e/board.feature
и добавьте в него следующее содержимое:
Feature: Board functionality Scenario: Create a board Given I am on empty home page When I type and submit in the board name Then I should be redirected to the board detail
Теперь нам необходимо создать определения шагов для каждого этапа сценария. Самый простой способ определить шаги – создать новый файл board.ts
в папке cypress/e2e
, который может выглядеть примерно так:
import { When, Then, Given } from "@badeball/cypress-cucumber-preprocessor"; Given("I am on empty home page", () => { cy.visit("/"); }); When("I type and submit in the board name", () => { cy.get("[data-cy=first-board]").type('new board{enter}'); }); Then("I should be redirected to the board detail", () => { cy.location("pathname").should('match', /\/board\/\d/); });
Вы можете поместить свой файл определений board.ts
в папку cypress/e2e
либо выбрать другое имя и поместить его в папку cypress/e2e/board
или в cypress/support/step_definitions
, после чего препроцессор cucumber автоматически их подхватит. Для пользовательского пути вам необходимо явно указать это в конфигурации (к ней мы перейдем немного позже).
Для улучшения работы при написании тестов в VS Code рекомендуется установить это расширение. Оно обеспечит корректную подсветку в файлах
.feature
и легкий доступ к определениям шагов.
Добавление параметров к определениям шагов
Определения шагов могут принимать параметры, что позволяет создавать более гибкие и многократно используемые сценарии тестирования. Давайте перепишем предыдущий файл определения шага так, чтобы можно было передавать в тест собственное имя:
import { When, Then, Given } from "@badeball/cypress-cucumber-preprocessor"; Given("I am on empty home page", () => { cy.visit("/"); }); When("I type in {string} and submit", (boardName) => { cy.get("[data-cy=first-board]").type(`${boardName}{enter}`); }); Then("I should be redirected to the board detail", () => { cy.location("pathname").should('match', /\/board\/\d/); });
Параметры автоматически передаются в соответствующие функции определения шага в качестве аргументов. Обратите внимание на {string}
в определении шага. Это позволит проверить, правильный ли тип мы передаем в наш шаг.
Теперь создадим в нашем файле cypress/e2e/board.feature
сценарий, принимающий в качестве параметра имя доски boardName
. Выглядеть это будет примерно так:
Feature: Board functionality Scenario: Create a board Given I am on empty home page When I type in "my board" and submit Then I should be redirected to the board detail
Тестирование на основе данных
Еще одним важным понятием в Сucumber, которое необходимо знать, являются таблицы данных. DataTable
в синтаксисе Gherkin позволяет передавать таблицу данных на шаг, что облегчает работу с несколькими наборами данных в тестовых сценариях. Это особенно полезно для тестирования на основе данных, когда необходимо проверить один и тот же сценарий с разными наборами входных данных.
Таблицы данных определяются в секции Examples
вашего файла .feature
. Продолжим работу с предыдущим файлом:
Feature: Board functionality Scenario: Creating a <listName> list within a board Given I am on empty home page When I type in "<boardName>" and submit And Create a list with the name "<listName>" Then I should be redirected to the board detail Examples: | boardName | listName | | Shopping list | Groceries | | Rocket launch | Preflight checks |
Определив шаги Examples
, вы будете запускать свой тест несколько раз, передавая на каждом шаге разные данные. Обратите внимание, как мы создаем переменные boardName
и listName
, оборачиваем их в <>
для передачи в качестве параметров в определениях шагов.
Рабочий массив данных
Эти таблицы данных также могут использоваться для подачи данных на один шаг, как показано в следующем примере:
Feature: Creating cards functionality Scenario: Create multiple cards Given I am in board detail When I create cards with names | Milk | Bread | Butter | Jam | Then 4 cards are visible
Однако шаг должен уметь классифицировать таблицу данных. Вот как это можно сделать:
When("I create cards with names", (table: DataTable) => { cy.get('[data-cy="new-card"]') .click() table.raw()[0].forEach(item => { cy.get('[data-cy="new-card-input"]') .type(`${item}{enter}`) }) });
Функция table.raw()[0]
вернет первую строку таблицы ([0]
) в виде массива. Внутри определения шага мы перебираем этот массив для создания элементов в списке.
Группировка тестов
Помимо ключевых слов Given
, When
, Then
и And
существует еще несколько способов организации нескольких тестов в одном файле .feature
. До сих пор наш тест создавал новую доску и новый список, но давайте немного изменим его и создадим один тест, который просто создаст еще одну доску и поместит ее перед нашим существующим тестом:
Feature: Board functionality Scenario: Opening a board Given I am on empty home page When I type in "<boardName>" and submit Then I should be redirected to the board detail Scenario: Creating a <listName> list within a board Given I am on empty home page When I type in "<boardName>" and submit And Create a list with the name "<listName>" Then I should be redirected to the board detail Examples: | boardName | listName | | Shopping list | Groceries | | Rocket launch | Preflight checks |
Аналогично блокам describe()
, context()
и it()
в Mocha мы можем дополнительно организовать наши тесты и сгруппировать их в логические кластеры. Ключевое слово Feature
выступает в роли блока describe(
https://mochajs.org/)
и служит группой верхнего уровня.
Внутри области видимости Feature
можно добавить блок Rule
, который еще больше разделит ваши сценарии на подгруппы.
По мере тестирования различных сценариев можно добавить шаг Background
, который будет действовать подобно хуку beforeEach()
в Mocha и запускать последовательность шагов перед каждым сценарием. Мы можем абстрагировать шаги Given
и When
от нашего текущего файла .feature
и сделать наш тест немного чище.
В сочетании с ключевым словом Rule
он может выглядеть примерно так:
Feature: Board functionality Rule: Happy paths Background: Empty board page Given I am on empty home page Scenario: Opening a board When I type in "new board" and submit Then I should be redirected to the board detail Scenario: Creating a <listName> list within a board When I type in "<boardName>" and submit And Create a list with the name "<listName>" Then I should be redirected to the board detail Examples: | boardName | listName | | Shopping list | Groceries | | Rocket launch | Preflight checks |
Использование хуков
Пока есть возможность добавить Background
, мы все еще можем определить шаги Before
и After
, которые действуют как хуки beforeEach()
и afterEach()
в Mocha. Сбой в них не приведет к ошибкам в тестах, так как они фактически выполняются внутри тестов.
Шаги Before
и After
являются частью файла определения шагов, что означает, что их не требуется добавлять в файл .feature
.
import { When, Then, Given, Before } from "@badeball/cypress-cucumber-preprocessor"; Before(() => { // reset application cy.request('POST', '/api/reset') }) Given("I am on empty home page", () => { cy.visit("/"); }); When("I type in {string} and submit", (boardName) => { cy.get("[data-cy=first-board]").type(`${boardName}{enter}`); }); When("Create a list with the name {string}", (listName) => { cy.get('[data-cy="add-list-input"]').type(`${listName}{enter}`); }); Then("I should be redirected to the board detail", () => { cy.location("pathname").should('match', /\/board\/\d/); });
Тегирование тестов
Теги – это мощная функция синтаксиса Cucumber, которая позволяет классифицировать и фильтровать сценарии. С их помощью можно запускать определенные сценарии или исключать их из процесса тестирования.
Чтобы добавить теги к сценариям, просто добавьте к сценарию или функции символ @, а затем имя тега. Например, добавим тег @regression
к сценарию успешного входа в систему в файле cypress/e2e/board.feature:
Feature: Board functionality Rule: Happy paths Background: Empty board page Given I am on empty home page @smoke Scenario: Opening a board When I type in "new board" and submit Then I should be redirected to the board detail Scenario: Creating a <listName> list within a board When I type in "<boardName>" and submit And Create a list with the name "<listName>" Then I should be redirected to the board detail Examples: | boardName | listName | | Shopping list | Groceries | | Rocket launch | Preflight checks |
Чтобы запустить тесты с определенным тегом, используйте следующую команду:
npx cypress run --env tags="@smoke"
При этом будут пропущены тесты, не содержащие тег @smoke
.
Вы также можете проверить это в режиме open
, используя ту же команду, но с open
вместо run
.
Помимо запуска всех тестов с определенным тегом, можно передать ключевое слово not
для запуска всех тегов, кроме указанного.
npx cypress run --env tags="not @smoke"
Существует также способ запуска всех тестов, содержащих любой из этих тегов:
npx cypress run --env tags="@smoke or @regression"
Или тесты, содержащие и то, и другое одновременно:
npx cypress run --env tags="@smoke and @regression"
Для ускорения выполнения теста можно использовать опции filterSpecs
и omitFiltered
, которые работают аналогично тому, как работает плагин @cypress/grep
. Вы можете включить эту функциональность, добавив следующие опции в файл cypress.config.ts
:
import { defineConfig } from "cypress"; import createBundler from "@bahmutov/cypress-esbuild-preprocessor"; import { addCucumberPreprocessorPlugin } from "@badeball/cypress-cucumber-preprocessor"; import createEsbuildPlugin from "@badeball/cypress-cucumber-preprocessor/esbuild"; export default defineConfig({ e2e: { specPattern: "**/*.feature", async setupNodeEvents( on: Cypress.PluginEvents, config: Cypress.PluginConfigOptions ): Promise<Cypress.PluginConfigOptions> { await addCucumberPreprocessorPlugin(on, config); on( "file:preprocessor", createBundler({ plugins: [createEsbuildPlugin(config)], }) ); return config; }, env: { omitFiltered: true, filterSpecs: true }, fixturesFolder: false, baseUrl: 'http://localhost:3000' }, });
Конфигурация
Существует два способа изменения стандартной конфигурации препроцессора Cucumber. Можно создать конфигурационный файл .cypress-cucumber-preprocessorrc.json
, который будет выглядеть следующим образом:
{ "stepDefinitions": [ "cypress/e2e/[filepath]/**/*.{js,ts}", "cypress/e2e/[filepath].{js,ts}", "cypress/support/step_definitions/**/*.{js,ts}", ] }
Или установить все прямо в package.json
, добавив эквивалент:
// rest of file skipped for brevity "cypress-cucumber-preprocessor": { "stepDefinitions": [ "cypress/e2e/[filepath]/**/*.{js,ts}", "cypress/e2e/[filepath].{js,ts}", "cypress/support/step_definitions/**/*.{js,ts}", ] }
Настройки из примеров являются значениями по умолчанию. Если вы не хотите ничего менять, то нет необходимости добавлять это в свой проект.
Отчетность
Плагин Cucumber для Cypress поставляется с различными вариантами настройки репортеров. Самый простой из них – HTML-репортер.
Практически все, что нужно сделать, – это настроить конфигурацию:
{ "html": { "enabled": true } }
После выполнения теста вы получите HTML-отчет с красивым форматированием, который выглядит следующим образом:
Если вам нужен более продвинутый вывод, который впоследствии вы захотите разобрать и передать в собственную систему отчетности, рекомендую обратить внимание на json-formatter
от авторов Сucumber. Вам потребуется установить его отдельно и настроить на запуск в конфигурационном файле.
Заключение
Как уже упоминалось в самом начале, существуют и более эффективные способы использования Cypress. Выполнять все сквозные тесты с помощью UI – это небольшое излишество, а учитывая дизайн Cypress, можно раскрыть гораздо больше возможностей, если использовать его так, как он задумывался.
Тем не менее, хочется верить, что эта статья была полезна для вас, если вы собираетесь использовать Cucumber вместе с Cypress.
Пингбэк: Перезапуски в автотестах