В INIT Group мы начали использовать Cypress и Cucumber в одном из проектов нашей команды около пары месяцев назад и считаем, что каким-то образом справились с этим процессом. И нам это нравится. Есть несколько шагов постепенного обучения, чтобы интегрировать его в процесс разработки. Но это действительно довольно просто, если вы их знаете. Я считаю, что любая команда может начать такую ​​работу через 2–4 недели. Вот почему я пишу этот пост.

Здесь вы можете найти репозиторий git скелета всего, что вам нужно для начала, с объяснением всех этих шагов пошагового обучения. Он протестирован на MacOS и Linux. Мы думаем, что некоторые части могут не работать с Windows.

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

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

Готовый? Тогда позвольте мне рассказать вам, как мы используем Cypress и Cucumber вместе так легко, что это может сделать каждый.

Как установить репозиторий скелет / пример

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

git clone [email protected]:jmarti-theinit/cypress-cucumber-example.git
git submodule init
git submodule update
npm install

После того, как вы установили все зависимости, продолжайте и выполняйте тесты:

npm run test:prod

Вы должны увидеть, что все 3 спецификации пройдены. Молодцы, давайте посмотрим, как это работает!

Где мы пишем фичи: используя внешний репозиторий «gherkin-features»

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

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

Вот почему мы решили использовать внешний репозиторий для функций. В только что загруженном скелетном репозитории репозиторий функций (в качестве примера): [email protected]: jmarti-theinit / cypress-cucumber-examples-features.git

То, как мы синхронизируем функции в этом репо с функциями в проекте Cypress + Cucumber, выглядит следующим образом:

  • Репозиторий включен как подмодуль git в папку «gherkin-features». Вот почему вам нужно сначала «git submodule init».
  • Функции внутри этой папки синхронизируются с папкой cypress / integration с помощью команды npm run test:pull-features, которая: (1) извлекает и обновляет подмодуль gherkin-features, (2) копирует новые функции, (3) перезаписывает любую функцию, измененную в cypress / integration и (4) удаляет любую функцию в cypress / integration, которая не существует в gherkin-features. Проверьте сценарий test: pull-features в package.json, чтобы увидеть, как все это делается.

Аналогом этого является то, что любое изменение функций должно производиться в исходном репозитории git, а не в gherkin-features или cypress / integration. В какой-то момент это может быть немного утомительно, но польза от этого выше.

Каждый раз, когда мы хотим создать новый тест, нам просто нужно npm run test:pull-feature раньше.

Это работает с подмодулями GIT. Если вы используете другую систему управления версиями, вам следует проверить, как заставить ее работать.

Если вы видите другой способ сделать это лучше, поделитесь с нами своим мнением.

Если вы хотите использовать собственное репозиторий gherkin-features, просто удалите текущий и добавьте свой в папку «gherkin-features» следующим образом:

git submodule deinit gherkin-features
git submodule add (YOUR_REPO_URL) gherkin-features
git add --all
git commit -m "Change repo url"

Если вы хотите узнать, как работают подмодули GIT, перейдите по этой ссылке. Я думаю, это очень хорошо объяснено: https://git-scm.com/book/en/v2/Git-Tools-Submodules

Учтите, что любое выполнение npm run test:pull-features должно быть добавлено, зафиксировано и отправлено в ваш репозиторий, поскольку подмодуль должен отражать все время, когда указывает на его собственный репозиторий git.

Как работают спецификации с огурцом и кипарисом

В этом репо используется cypress-cucumber-processor, который является плагином для Cypress. Установка действительно проста (уже сделана в только что загруженном репозитории):

  • В package.json вы можете найти зависимость.
"cypress-cucumber-preprocessor": "^1.9.1",
  • В cypress / plugins / index.js вы можете найти соединение с Cypress в качестве препроцессора файлов.
module.exports = (on, config) => {
  on('file:preprocessor', cucumber());
  ...
};
  • Вы также можете указать Cypress не показывать файлы javascript, а показывать только файлы .feature через cypress.json.
{
...
"ignoreTestFiles": ["*.js", "*.md"],
...
}

Способ организации функций и кода javascript довольно прост:

  • Все ваши тесты должны находиться в папке кипарис / интеграция.
  • На том же уровне, что и объект в этой папке, создайте папку с тем же именем, что и объект. Например, если у вас есть feel-lucky.feature, создайте папку feel-lucky ».
  • Внутри этой папки создайте столько файлов javascript, сколько сценариев есть у вашей функции.

Способ написания кода для шагов Cucumber в javascript также очень прост.

import { Given, When, Then } from 'cypress-cucumber-preprocessor/steps';
Given(/^One given step$/, () => {
   ...
});
Given(/^another given step$/, () => {
   ...
});
When(/^Some other when step$/, () => {
  ...
});

Then(/^I have some results$/, () => {
  ...
});

В javascript-файле сценария вы можете добавить столько шагов, сколько вам нужно. Они должны соответствовать шагам «Дано», «Когда», «Тогда» (и), которые указаны в соответствующем файле .feature.

Использование шаблона объекта страницы

Мне очень нравится Шаблон объекта страницы, подробнее о нем вы можете прочитать здесь. Этот шаблон обеспечивает удобство обслуживания, поскольку он предлагает инкапсулировать все элементы HTML (селекторы CSS, щелчки и т. Д.) В объект Page. Таким образом, когда вы выполняете несколько сценариев, связанных с одной и той же страницей, весь HTML-код находится в одном конкретном файле; и если эти тесты не работают из-за изменения HTML, вы можете перейти на эту страницу, изменить ее и исправить сразу несколько тестов.

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

Given(/^I'm at Google$/, () => {
  GoogleSearchPage.visit();
});

When(/^I type search word 'github'$/, () => {
  GoogleSearchPage.type('github');
});
When(/^Press 'Search'$/, () => {
  GoogleSearchPage.pressSearch();
});

Then(/^I have some results$/, () => {
  GoogleResultsPage.expect().toHaveResults();
});

И на вашей странице у вас есть этот код. Как видите, вначале мы обычно раскрываем весь связанный с HTML код через константы, чтобы его было легче найти.

const SEARCH_FIELD = 'input[type=text]';
const SEARCH_BUTTON = 'input[type=submit]';
const SEARCH_TEXT = 'Buscar';

class GoogleSearchPage {
  static visit() {
    cy.visit('/');
  }
  static type(query) {
    cy.get(SEARCH_FIELD) // 2 seconds
      .type(query);
  }
  static pressSearch() {
    cy.get(SEARCH_BUTTON).contains(SEARCH_TEXT)
      .click();
    return new GoogleResultsPage();
  }
}

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

Использование общих шагов огурца

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

Огурец позволяет использовать обычные шаги. С препроцессором Cypress и Cucumber их нужно поместить в папку cypress / integration / common. Мне не очень нравится эта структура, но это то, что вы можете сделать прямо сейчас.

Вы можете добавить столько файлов javascript, сколько захотите, в папку cypress / integration / common и столько шагов внутри каждого из этих js файлов, сколько захотите.

Обычно мы стараемся использовать визуально иерархическое имя файлов. Например, «google.js» для шагов, общих для папки «google». «Google-search.js» для шагов, общих для «google / search.js» и т. Д.

С нашей точки зрения, было бы лучше, если бы этот общий каталог можно было добавить в каталог cypress / integration / google, как в примере выше. Но сейчас это недостижимо (или мы этого не достигли).

Приспособления сервера: установка сервера в известное состояние для тестов

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

В нашем случае мы настроили этот фреймворк:

  • Мы создали модуль «команды базы данных e2e» в нашем бэкэнде. Ответственность этого модуля заключается в предоставлении API для выполнения команд базы данных для этих сквозных тестов.
  • Команда базы данных - это набор операций с базой данных. Команда базы данных вызывается через строковое имя. Например, «set-test-user-height-to-175» равно «update user set height = 175, где id = 1».
  • У нас есть в базе данных несколько предустановленных приспособлений, на которые вы можете рассчитывать. В этом примере файл user. Это все, что у нас есть.
  • Мы храним команды в базе данных и можем вызывать их через открытый API. Таким образом, из нашего кода узла в cypress мы делаем executeCommand('set-test-user-height-to-175') и знаем, что у пользователя будет эта высота для тестов.
  • Мы создаем новые команды по мере необходимости в наших тестах.

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

С точки зрения Cypress и Cucumber единственная проблема, которую нам нужно было решить, это то, что нам нужно было вызывать эти команды (или запросы) при настройке данных тестов. И поскольку это асинхронные операции, мы должны убедиться, что эти операции выполняются синхронно, прежде чем приступать к тестам.

Для этого у кипариса есть мощная «cy.task». Он гарантирует, что все, что вы делаете, находится внутри механизма цепочки Cypress. В только что загруженном репозитории вы можете проверить, как мы вызываем асинхронную задачу, которая занимает 5 секунд, и как Cypress ожидает ее, прежде чем продолжить.

const executeCommand = (command) => {
  cy.task('pluginExecuteCommand', command);
};
module.exports = (on, config) => {
  ...
  on('task', {
    pluginExecuteCommand,
  });
  ...
};
  • И вы можете увидеть pluginExecuteCommand в cypress / plugins / plugin-execute-command.js. Убедитесь, что это всегда возвращает обещание, чтобы Cypress его ждал. В нашем примере мы выполняем асинхронный сон в течение 5 секунд. Это должно быть заменено вашим вызовом вашего API из задания базы данных e2e (вы можете использовать свою собственную структуру запросов с node, например, axios; или командами cy.request).
const pluginExecuteCommand = command =>
  new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(`${command} execution simulated after 5 secs`);
    }, 5000);
  });

Это то, что, вероятно, займет у вас больше всего времени, чтобы начать с кипариса и огурца. Делайте это постепенно, начните предоставлять API для выполнения команды, и в будущем вы добавите более богатые функции (параметризованные запросы, создание шаблонов этих команд базы данных и т. Д.).

Запуск тестов в разных средах

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

Чтобы справиться с этим, Cypress предлагает отличную функциональность:

  • По умолчанию он использует переменные cypress.json в вашей корневой папке.
  • Но cypress / plugins / index.js может перезаписывать нужные вам переменные, возвращая объект теми, которые вы хотите перезаписать.

В нашем случае с помощью скриптов npm мы устанавливаем среду CYPRESS_ENV при вызове тестов (определенная нами, она не связана с CYPRESS).

Затем возвращаем его в cypress / plugins / index.js:

module.exports = (on, config) => {
  ..
  // `config` is the resolved Cypress config
  return cypressConfigResolver();
};

Эта функция cypressConfigResolver определена в файле cypress / config / cypress-config-resolver.js. Что читает xxxxxx.json в каталоге cypress / config в соответствии с файлом CYPRESS_ENV (или localhost, если не определен).

Этот cypressConfigResolver также используется в cy.tasks, которые вызывают задания базы данных e2e (например, в pluginExecuteCommand); так как вам нужно убедиться, что фикстуры и данные установлены только на серверах, с которыми вы проводите тесты.

Таким образом, мы можем открыть кипарисовик (обычно при разработке):

npm run cypress:open:local
npm run cypress:open:prod

Или мы можем запустить такие тесты без головы (как правило, чтобы убедиться, что мы не сломали ни одного теста):

npm run test:local
npm run test:prod

Или мы можем запустить тесты с помощью Chrome, например (для отладки):

npm run test:debug:local
npm run test:debug:prod

Вы можете проверить, что он работает в текущем репозитории, потому что, когда вы выполните npm run cypress:open:pro, вы увидите, что Cypress открывается с www.google.com и API executeCommand работает с https://some-server-production.com/ e2e-api / », как это определено cypress / config / production.json .

В отличие от этого, мы выполняем npm run cypress:open:local, в котором вы видите следующее, основанное на средах cypress / config / localhost.json.

Интеграция с Jenkins

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

Чтобы каждый мог запускать тесты и иметь возможность запускать их автоматически, мы создали конвейер. Определение конвейера включено в репозиторий в файле build / Jenkinsfile.

В этой конфигурации конвейера примечательны три вещи:

  • Выполняются только тесты, отмеченные @ e2e-test. Это сделано потому, что нам нужно исключить тесты, которые были написаны для будущих пользовательских историй и еще не автоматизированы; чтобы неавтоматизированные тесты не выдавали ошибок. Для этого мы говорим Cypress выполнять только тесты, помеченные как @ e2e-tags. Это делается с помощью сценария npm run test в package.json.
{
...
"test": "cypress run --env TAGS='@e2e-test' --spec 'cypress/integration/**/*.feature'",
...
}
  • Мы также хотим, чтобы отчеты об испытаниях можно было легко читать после выполнения. Для этого мы используем репортера. Поскольку Cypress использует мокко для внутренних целей, достаточно сказать Cypress, чтобы он использовал репортер «junit». И мы говорим Дженкинсу через конвейер использовать эти отчеты.

В cypress.json:

{
  ... 
  "reporter": "junit",
  "reporterOptions": {
    "mochaFile": "test-results/test-output-[hash].xml"
  },
  ...
}

В Jenkinsfile:

{ 
...
  post {
    always {
      junit keepLongStdio: true, testResults: 'test-results/*.xml', allowEmptyResults: true
    }
  }
...
}
  • Мы также хотим экспортировать видео с Cypress, чтобы иметь возможность легко загружать их в случае чего. Для этого мы выставляем эти артефакты через Jenkinsfile.
post {
  always {
    ...
    archiveArtifacts artifacts: 'cypress/videos/**/*.mp4', onlyIfSuccessful: false
    ...
  }
}

Интеграция с IntelliJ IDEA

Лучший способ, который мы нашли для интеграции Cypress и Cucumber с IntelliJ IDEA, - это установить плагин Cucumber.js.

Но мы находим довольно неприятные недостатки:

  • Мы не можем создавать шаги из среды IDE, поскольку она не распознает структуру тестов и не настраивается.
  • Когда мы запускаем IDE, она не сразу распознает функции. Нам нужно внести некоторые изменения в файл функций и файл js, чтобы найти его (вероятно, он индексирует шаги только при изменении файлов, но не при загрузке IDE).

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

Жалко, что для этого нет поддержки.

Как сделать так, чтобы все разработчики и владельцы продуктов подключились к новому рабочему процессу разработки

Мы используем Cypress + Cucumber в качестве инструмента приемочного тестирования, и все идет хорошо. Это означает, что в нашем рабочем процессе все пользовательские истории имеют спецификации, а приемочные испытания всех реализованных функций автоматизированы.

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

  • Спецификации не предназначены для однонаправленного написания и реализации. Это примеры разговоров, которые явно записаны в пользовательских историях. Когда пользовательские истории сложны, мы можем провести сеанс Пример сопоставления, чтобы развеять сомнения.
  • Все истории должны иметь, по крайней мере, файл спецификации перед разработкой (указывающий на репозиторий gherkin-features). Если у них его нет, любой может добавить новую спецификацию, а не только владелец продукта.
  • Нам нужно убедиться, что приемочные испытания автоматизированы. Для этого мы создали новый столбец «Автоматизированное тестирование» на нашей доске Kanban / JIRA / Sprint. Мы не гордимся этим, так как мы уже должны знать, что «Готово» означает «с автоматизированными тестами». Но поскольку эта практика является чем-то новым в команде, мы решили явно объявить ее в столбце. Возможно, в будущем эта колонка исчезнет.

Непрерывная доставка и откат

Если вы читаете эту статью, чтобы узнать, как мы автоматически запускаем тесты на нашем сервере и выполняем откат в случае сбоя, у меня для вас плохие новости: нам все еще не хватает этой функции.

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

Выводы и как я бы вам порекомендовал начать

За эти первые два месяца мы достигли того, чего давно желали: (1) автоматизировать наши приемочные тесты языком, понятным даже деловым людям, и (2) с помощью этих тесты.

Для нас это было настоящим достижением, и после отражения всего того, что мы сделали, мы действительно верим, что это достижимо для всех теперь, когда появились технологии Cypress и Cucumber, и у них есть такой легкий входной барьер. Они действительно верят, поверьте мне.

Если вы хотите, чтобы вы и ваша команда достигли этого за 2–4 недели, я бы посоветовал вам сделать следующее:

  • Прежде всего, поговорите внутри своей команды о том, зачем вам это нужно. Убедитесь, что вы все верите, что это необходимо и что это возможно. Убедитесь, что все понимают, что это немного замедлит вас, по крайней мере, на пару месяцев вначале. Да, вы создадите несколько новых пользовательских историй, но вначале вы будете делать их медленнее.
  • Я бы порекомендовал одному человеку (владельцу продукта? Специалисту по тестированию?) Начать создавать пару спецификаций для ваших следующих пользовательских историй, просто чтобы попрактиковаться в Gherkin. Это должно быть «Упоминание о корнишоне». Начните с самых простых. Это позволит этому «специалисту» помочь разработчикам позже написать некоторые спецификации.
  • Создайте отдельный репозиторий для e2e-тестов. Клонировать мой репозиторий и использовать его как каркас. Удалите мое репозиторий gherkin-features и используйте свой. Если вначале внешний репозиторий «gherkin-features» добавляет ненужную сложность, забудьте об этом и добавьте функции в обычную папку gherkin-features или даже в папку cypress / integration.
  • Проведите сессию в своей команде, чтобы убедиться, что все понимают основы этого репозитория и то, как работает Cypress.
  • Разработайте несколько базовых тестов с их спецификациями, основанными на прошлых функциях, а не на будущих. Убедитесь, что каждый разработчик в команде создал и разработал спецификацию (с помощью этого Справочного лица).
  • В этот момент убедитесь, что у владельца продукта есть немало будущих пользовательских историй со спецификациями.
  • После того, как каждый разработчик разработал хотя бы спецификацию, добавьте на свою доску новый столбец «Тестирование автоматизировано».
  • Таким образом, вы должны начать разработку пользовательских историй со спецификациями.
  • С этого момента убедитесь, что ни одна будущая пользовательская история не обходится без файла спецификации, и убедитесь, что все разработанные пользовательские истории проходят «автоматическое тестирование».
  • Добавьте задание об этом на сервер интеграции, чтобы убедиться, что все визуализируют увеличивающуюся «кобертуру» тестов (в зависимости от количества выполненных тестов). Запустите его и попробуйте автоматизировать.

Следует помнить о некоторых вещах:

  • Если у вас разные интерфейсы, я бы посоветовал сосредоточиться только на одном (наиболее распространенном). И повторите все это со вторым после того, как освоите этот первый интерфейс.
  • Неважно, проводите ли вы тесты до или после. Вы можете делать BDD во время разработки, а можете нет. Просто убедитесь, что все тесты автоматизированы при прохождении через столбец «Test Automated».
  • Для нас очень часто бывает, что файлы спецификаций несовершенны или их нужно изменять во время разработки. Каждый должен иметь право изменять любую спецификацию по мере необходимости. Если спецификация неверна, нет оправдания, чтобы остановить автоматизацию теста. На самом деле, вероятно, это будет неправильно, но, по крайней мере, это будет отражать необходимость, лежащую в основе пользовательской истории.

Это более или менее те шаги, которые сработали для нас, в нашем контексте и в нашей команде. И мы вполне довольны результатом. Не упустите возможность попробовать это в своей команде!

Если вы выполнили эти или другие шаги, пожалуйста, не стесняйтесь оставлять отзывы. Мы здесь, чтобы учиться!

Спасибо!