1. Введение
Cucumber уже не молодой и вероятно не самый популярный инструмент для разработки, ориентированной на BDD, но он все еще используется некоторой популярностью так как фокусируется на определении и тестировании ожидаемого поведения системы с точки зрения пользователей.
Я решил использовать связку из этих двух инструментов исходя из того что BDD тесты – это простой текст, на человеческом языке, написанный в форме истории (сценария), описывающей некоторое поведение, значит Cucumber прост для понимания как начинающему специалисту с небольшим опытом тестирования так и для лица технически не подкованным. При этом мощная сердцевина playwright дает большие возможности для написания авто тестов. Градация фреймворка на части позволит опытному специалисту создавать базовую часть функций (в данном примере на Playwright + TypeScript), а начинающему специалисту покрывать тестами приложение с использованием готовой базы и знаниями (Cucumber, X-path). PS возможно это станет более понятным в конце статьи.
Используемые фреймворки/инструменты:
-
Playwright
-
Cucumber
-
Typescript
-
NodeJS
2. Подготовительные работы
Для практики нам понадобиться простенькое web приложение / сайт. На пример это будет тестирование UI части некой signup/registration формы.
Оптимальный вариант запуск данного web приложения локально.
Вы можете как самостоятельно создать похожую страницу так и воспользоваться примером созданным мною. Желательно Чтобы сильно не загружать статью файлами, необходимый комплект файлов добавлен в git репозиторий -https://github.com/QaitkenQ/test_signup_form/tree/start_files
Шаги установки/настройки web приложения
-
Установите Node JS.
-
Создайте папку/каталог, например test_server.
-
Распакуйте скачанные файлы в созданный каталог или загрузите файлы через консоль посредством git.
Корневой каталог с web приложением будет выглядеть так: (*readme.md файл не обязателен) -
Откройте командную строку и перейдите в рабочий каталог или откройте каталог редактором кода (например VSC).
-
Выполните следующие команды через терминал.
-
npm init ‑y
-
npm install express body-parser
-
-
Запустите сервер командой
-
node server.js
-
-
Убедитесь в работоспособности сервера открыв страницу http://localhost:3000 в браузере.
Сайт готов для выполнения тестирования.
3. Установка/настройка Playwright и Cucumber
-
Создайте корневой каталог для фреймворка со следующей структурой (например Cucuwright):
Папка feature - содержит файл сценариев Cucumber.
Папка src - содержит 3 папки с файлами:
1. step_defenitions - здесь находятся файлы c детальным описанием шагов cucumber.
2. page_objects - здесь находятся файлы с описанием элементов страницы и действия выполняемые с ними.
3. utils - здесь находится файл BaseFunctions содержащий базовые операции/действия, которые могут быть применимы для любой страницы.
Файл cucumber.js - содержит некоторые настройки для фреймворка Cucumber.
Файл tsconfig.json - содержит настройки TypeScript.Стартовый комплект файлов так же доступен в репозитории на другой ветке https://github.com/QaitkenQ/test_signup_form/tree/cucuwright
В них уже имеются предварительно созданные данные для начала тестирования страницы с singup form, которая уже запущена на http://localhost:3000 (Например файл шагов уже содержит хуки BeforeAll и AfterAll для корректного открытия и закрытия страницы браузера, необходимые зависимости между файлами уже описаны). -
Откройте командную строку и перейдите в рабочий каталог или откройте каталог редактором кода (например VSC).
-
Установите фреймворк Playwright выполнив команду
npm init playwright@latest
и выбрав необходимые опции (язык typescript, каталог для тестов и т.п.) -
Установите фреймворк Cucumber выполнив команду
npm i @cucumber/cucumber
. -
Установите дополнительные библиотеки при необходимости
npm install typescript
npm install ts-node
-
Созданные каталоги tests и tests-examples можно удалить.
-
Проверьте работоспособность запустив команду
npx cucumber-js
В консоль должно вывести сообщение о том что для текущего сценария шаги не определены. Файл singup_page.feature имеет один сценарий с 2 шагами. Значит все инструменты корректно установлены и зависимости работают.
-
По желанию можно установить несколько расширений Cucumber для VSC.
Cucumber (Gherkin)
Cucumber Reference Support
4. Написание авто теста
Открываем файл сценариев signup_page.feature По умолчанию в нем уже присутствует 1 сценарий с 2 шагами. При создании нового файла .feature создайте аналогичный сценарий.
Сценарий 1
Шаг 1 - открытие страницы
Откроем файл signup_page_steps.ts и создадим основное описание для первого шага Given SignupPage open page
Откроем файл signup_page.ts, добавим url нашей страницы и создадим функцию openPage() для выполнения операции открытия страницы.
Все стандартные операции действия которые возможно выполнять с различными элементами вынесены в отдельный файл BaseFunctions.ts в папке utils.
Функция openPage() включает в себя базовую функцию openUrl() и передает параметр url.
Откроем файл BaseFunctions.ts и создадим базовую функцию openUrl(), финальным действием является метод goto().
Для выполнения данной операции можно было описать все в одной функции openPage(), но мы соблюдаем структуру разделения на операции которые относятся только для данной страницы и операциям которые могу быть применимы к любой другой.
Проверим на работоспособность шаг 1 - запускаем тест npx cucumber-js
Шаг 2 - проверка заголовка
Перейдем в файл signup_page_steps.ts и создадим описание для второго шага
When SignupPage verify the header "Sign Up page"
В данном случаи мы передаем значение(название заголовка) как строку {string}. При любых изменениях текста потребуется только изменить значение в .feature файле.
В файле signup_page.ts, добавим локатор (X-path, или атрибут элемента) для заголовка и создадим функцию isHeadingVisible() для выполнения операции поиска элемента.
В базовую функцию findElement() мы передаем значение селектора уже с замененным текстом из первоначального шага When SignupPage verify the header
"Sign Up page". Посредством .replace значение %s
становится равным Sign Up page
.
Добавление функции findElement() в файл BaseFunctions.ts
Для данной операции был выбран метод waitForSelector().
Запускаем наш тест еще раз npx cucumber-js
Проверим точно ли мы тест выполняется, действительно ли нужная страница загрузилась и тест проверяет название заголовка - изменим значение в шаге 1 "Sign Up page" -> "Sign Up page Fail"
Получаем в консоли ошибку при выполнении шага 2 - Error: function timed out...
Похоже на правду, но хотелось бы получить больше информации в данном случае.
Перепишем функцию findElement() в файле BaseFunctions.ts. Добавим дополнительную функцию erifyElementExists(), которая будет выводить в консоль более детализированное описание об ошибке. Так же можно добавить значение таймаута.
Перезапускаем тест npx cucumber-js
Получаем в консоли другой результат - Error: Can't find the element '//h2[text()='Sign Up page Fail']' within the specified timeout
Теперь можно сразу проверить верный ли locator для элемента. Легко проверить скопировав этот путь из лога и вставив его в devtools.
Совпадений 0.
Но если удалить лишний текст, который мы добавили ранее, то будет найдет необходимый элемент.
справляем значение - возвращаем изначальное в шаге 1.
Перезапускаем тест - 1 scenario (1 passed), 2 steps (2 passed). Получаем желаемый результат.
Сценарий 2 - Проверка ярлыков(labels)
Шаг 1 остается прежним открытие страницы.
Шаг 2 - проверка названия поля элемента
Создаем новый шаг в singup_page.feature файле. Так как в форме много однотипных элементов, то желательно создать шаг с использованием таблицы. Позже когда он будет выполнен успешно, мы добавим остальные ярлыки которые есть в форме. Для начала возьмем ярлык от первого поля First Name
В файле singup_page_steps.ts описываем шаг 2 с учетом передачи значений из таблицы.
В файле singup_page.ts создаем функцию verifyLabels() и добавляем селектор для элемента label.
Данная функция использует уже имеющуюся базовую функцию findElement() из BaseFunctions.ts.
Так как у нас теперь 2 сценария, то запускаем только второй сценарий по тегу @test2 npx cucumber-js --tags=@test2
Результат тест пройден.
Добавим в таблицу остальные ярлыки которые есть в форме
Перезапускаем сценарий 2 npx cucumber-js --tags=@test2
Результат - мы получили ошибку. Последний ярлык из таблицы "Accept learning the Cucuwright" - не может быть найден.
Выполнив небольшой анализ видно что ярлык для элемента checkbox имеет другую структуру в DOM дереве. Значит придется создать отдельный шаг для данного элемента.
Создаем новый шаг в файле .feature. Создадим шаг с передачей табличных значений на случай использование данного метода для страницы с несколькими элементами checkbox.
В файле singup_page_steps.ts добавим описание шага
В файле singup_page.ts создаем функцию verifyCheckbox() и добавляем селектор для элемента checkbox_label. В селекторе мы использовали X-path для более детального уточнения элемента - элемент должен иметь тип checkbox с необходимым текстом.
Запускаем тест npx cucumber-js --tags=@test2
Результат - сценарий выполнен успешно.
Для перепроверки можно выполнить быстрый дебаг добавив ошибку в значение(название) ярлыка - Accept learning the Cucuwright -> Accept learning th Cucuwright
В результате мы получим ошибку что элемент checkbox с данным текстом не может быть найден.
Сценарий 3 - Проверка введенных значений в поле ввода
Новый сценарий и шаг в файле .feature
В файле singup_page_steps.ts добавим описание шага.
В файле singup_page.ts добавим локатор для поля First Name и создадим функцию enterUserName(). Данная функция будет передавать данные в виде string базовой функции enterText().
Создадим базовую функцию enterText() в файле BaseFunctions.
Проверим работоспособность теста npx cucumber-js --tags=@test3
Добавим следующий шаг к сценарию №3, проверка значения в поле ввода.
Новый шаг в фалйе .feature
В файле singup_page_steps.ts добавим описание шага.
Для примера сделаем этот шаг универсальным и применимым к другим полям ввода. К дополнению к основной функции verifyFieldValue() создадим дополнительную функцию determineField() определяющую поле ввода. Так же необходимо добавить локаторы для остальных полей из формы в singup_page.ts файле.
В зависимости от значения в шаге .feature файла будет определено поля для проверки и в случаи отсутствия его в списке для проверки будет выведена ошибка.
Далее в в файле BaseFunctions необходимо создать функцию verifyValue().
Запускаем тест еще раз npx cucumber-js --tags=@test3
Результат - тест выполнен успешно.
Небольшой дебаг - попробуем ввести ошибочное название поля First Name -> Fail Name
В результате мы получим ошибку с описанием, которое добавили немного ранее.Unknown field 'Fail Name', please check the name
Сценарий 4 - проверка radio button элемента
Новый сценарий и шаг в файле .feature
Файл singup_page_steps.ts - описание шага.
Файл singup_page.ts - добавление локатора radio_btn и создание функции selectRadioButton().
Файл BaseFunctions - cоздание базовой функции selectItem().
Запуск теста npx cucumber-js --tags=@test4
Результат - тест выполнен успешно.
Добавим следующий шаг для проверки выбранного значения.
Файл singup_page_steps.ts - описание шага.
Файл singup_page.ts - создание функции isRadioChecked().
Файл BaseFunctions - cоздание базовой функции isChecked().
Повторный запуск теста npx cucumber-js --tags=@test4
Результат - тест выполнен успешно.
Для перепроверки добавим в шаг в табличное значение выбор следующего элемента radio button female.
В данном случае результатом будет ошибка сообщающая о том, что элемент radio button male не выбран.
Сценарий 5 - проверка dropdown поля
Новый сценарий и шаг в файле .feature
Файл singup_page_steps.ts - описание шага.
Файл singup_page.ts - создание функции selectDdlValue(). Локатор ddList уже был добавлен ранее.
Файл BaseFunctions - cоздание функции selectOption()
Запуск теста npx cucumber-js --tags=@test5
Результат - тест выполнен успешно.
Добавление следующего шага для проверки выбранного значения в выпадающем списке.
Новый шаг в файле .feature. Так как ранее был создан универсальный шаг, который применим к нашему выпадающему списку, создавать дополнительно ничего не требуется
Повторный запуск теста npx cucumber-js --tags=@test5
Результат - сценарий выполнен успешно.
Сценарий 6 - выбор checkbox'a
Новый сценарий и шаг в файле .feature
Файл singup_page_steps.ts - описание шага.
Файл singup_page.ts - создание локатора checkbox и функции selectCheckbox().
Базовая функция selectItem() уже была создана ранее.
Запуск теста npx cucumber-js --tags=@test6
Результат - тест выполнен успешно.
Добавление следующего шага для проверки был ли элемент выбран.
Новый сценарий и шаг в файле .feature
Файл singup_page_steps.ts - описание шага.
Файл singup_page.ts - создание локатора checkbox и функции selectCheckbox().
Базовая функция isChecked() уже была создана ранее.
Повторный запуск сценария npx cucumber-js --tags=@test6
Результат - сценарий выполнен успешно.
Сценарий 7 - проверка ошибки валидации.
Новый сценарий в файле .feature. Используем имеющиеся шаги и создаем новый шаг для нажатия кнопки Submit.
Файл singup_page_steps.ts - описание шага.
Файл singup_page.ts - создание локатора btn и функции clickButton().
Файл BaseFunctions - cоздание базовой функции clickBtn().
Запуск теста npx cucumber-js --tags=@test7
Результат - тест выполнен успешно.
Добавление новых шагов - проверка ошибки валидации.
Файл singup_page_steps.ts - описание шага.
Файл singup_page.ts - создание локатора valid_error и функции verifyError(). Так же как и в случае с проверкой значения в поле ввода в данной функции присутствует вспомогательная функция для распознавания пути - у какого элемента искать ошибку.
Файл BaseFunctions - cоздание базовой функции verifyText().
Запуск сценария npx cucumber-js --tags=@test7
Результат - тест выполнен успешно.
Добавим в данный сценарий проверку ошибки валидации для поля Last Name - "Required field"
Повторный запуск теста npx cucumber-js --tags=@test7
Результат - тест выполнен успешно.
Изменим текст ошибки для выполнения негативного сценария и перепроверки созданного теста
В результате получим ожидаемую ошибку. Фактический результат "Please use only alphabetical characters"
Заключение
В итоге мы настроили запустили простенький сервер, установили и настроили фреймворк и написали несколько авто тестов. В начале статьи я писал, что обьясню как в моем понимании связка cucumber + playwright может помочь в написании авто тестов начинающему специалисту. При использовании данной структуры и работе в команде опытный специалист может создать основной костяк на основе небольшой части приложения и описав основные операции в файле BaseFunctions. В случаи большого веб приложения и схожих его частей и использовании шагов с универсальными способами, второму специалисту, с меньшим опытом написания авто тестов, будет не сложно покрыть тестами остальную часть приложения на основе имеющегося задела. Как результат это будут созданные фалы для других страниц с дублированием имеющихся операций и минимальными изменениями. Хотелось показать на примере создания тестов для другой страницы, но статья уже получилась не маленькая и я думаю это будет лишним. Так же плюсом будет простота понимания написанных сценариев в файле .feature для технически не подкованного человека.
Это моя первая статья, прошу отнестись с пониманием если имеются неточности или ошибки. Буду рад любой критике. Надеюсь кому-то статья будет полезной. В будущем планирую создать другую статью с практическими тестами для БД или API.
Автор: Aitken