Доверие клиентов — приоритет для Tempest, поэтому один из основных принципов нашей работы — выпуск надежного и качественного программного обеспечения.

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

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

Почему автоматизированное тестирование?

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

Но поскольку с каждым релизом в процессе контроля качества вручную оставалось не выявлено все больше ошибок, мы тратили драгоценное время на сортировку и исправление неполадок непосредственно перед релизом (а иногда и после него). С учетом компактности нашей команды и постоянно расширяющейся сферы применения продукта продолжать вручную тестировать каждую функцию для каждого релиза было просто нецелесообразно и, честно говоря, не очень выгодно с точки зрения DevEx (Developer Experience — подход, ориентированный на простоту и скорость разработки).

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

Этап 1: Cucumber-js + библиотека Playwright

В нашем первоначальном стеке использовался Cucumber JS, поддерживаемый Playwright в качестве исполнителя тестов. По сравнению с прошлыми тестами, перегруженными шаблонами, использование синтаксиса псевдоестественного языка Gherkin было глотком свежего воздуха. Вот пример одного теста для функции Teams:

Feature: Teams

Background:
Given that I am signed in as the primary user
And that I have created a new organization using the Tempest API

@teams
Scenario: Creating a team
When I click the "Teams" link in the sidebar
Then the path with the org prefix should be "teams"
When I click "Create a Team"
Then the path with the org prefix should be "teams/create"
When I fill out the "Name..." input with "Web Platform Team"
And I click "Create Team"
Then the path with the org prefix should be "teams/*"
Then I should see the heading "Web Platform Team"

Но мы быстро столкнулись с проблемами конфигурирования:

  • Запуск Playwright в режиме библиотеки ограничивал доступные нам API и инструменты разработки. Передача параметров конфигурации через CLI Cucumber-js в Playwright приводила к появлению кучи шаблонного кода.
  • Заставить TypeScript и ES-модули эффективно работать с Cucumber-js было нетривиальной задачей. У нас не было подобного опыта, поскольку остальная часть нашей кодовой базы использует TypeScript нативно (Deno) или с нулевой конфигурацией с помощью Vite.
  • Некоторые тесты периодически выходили из строя при распараллеливании и работе на полной скорости, поэтому пришлось снизить скорость работы с помощью опции Playwright slowMo и запускать тесты последовательно.

Но даже несмотря на эти трудности, все получилось! Весь Tempest можно было запустить на одной машине, поэтому барьер для запуска набора тестов локально был относительно низким. После этого было несложно запустить набор в нашей среде непрерывной интеграции (CI).

Это позволило нам приступить к написанию тестов, но со временем трудности стали ощущаться все сильнее. Запуск сравнительно небольшого набора тестов в CI занимал 20 с лишним минут, а у разработчиков не было достаточного опыта по написанию тестов. Компанию, ориентированную на DevEx, с высокой планкой качества это не устраивало. Нужно было продолжать поиски решения.

Фаза 1.5: Playwright и генерация кода

Во время ночной отладки тестов мы наткнулись на Playwright-BDD, который, как казалось, мог бы предложить значимый шаг вперед. У нас появилась возможность запустить Playwright в «тестовом» режиме (то есть обычном) и воспользоваться всеми преимуществами той большой работы, которую проделала команда Playwright для упрощения написания тестов, и при этом использовать существующий каталог Gherkin-тестов.

Задаем Gherkin-тест:

Feature: Playwright site

Scenario: Check get started link
Given I am on home page
When I click link "Get started"
Then I see in title "Installation"

Генерация кода выдает такой тестовый файл:

// Сгенерировано из: sample.feature
import { test } from "playwright-bdd";

test.describe("Playwright site", () => {
test("Check get started link", async ({ Given, When, Then }) => {
await Given("I am on home page");
await When('I click link "Get started"');
await Then('I see in title "Installation"');
});
});

Playwright-BDD позволил изменить соотношение между Cucumber и Playwright. CLI Playwright-BDD выполнял парсинг наших существующих Gherkin-тестов и генерировал совместимые с Playwright тесты и код фикстур, которые затем запускались Playwright, как если бы это был написанный вручную тест. Playwright-BDD недавно отказался от cucumber-js-зависимости в пользу собственного парсера, что означает возвращение модулей TS и ES к выполнению своих функций.

Перенос первой группы тестов и наблюдение их выполнения в UI-режиме Playwright стало огромной победой. Возможность видеть, какие элементы кликаются, и визуально отлаживать тесты значительно улучшила качество работы. При сравнительно небольших затратах нам удалось устранить несколько самых серьезных проблем с существующим набором тестов.

Перенос большого количества тестов также позволил нам получить уникальное представление о состоянии всей кодовой базы, которого у нас не было при написании тестов во время активной разработки. Используя синтаксис Gherkin, вы жертвуете гибкостью в обмен на удобство чтения и повторное использование. Идея повторного использования селекторов (автоматическое взаимодействие, поддерживающее оператор Gherkin) в тестах заманчива. Идея конечного API для взаимодействия с приложением Tempest прекрасна в теории. Однако на практике мы постоянно сталкивались с рядом затрудняющих работу повторяющихся шаблонов. Главным из них было определение области взаимодействия селекторов и утверждений:

  • Для всех взаимодействий (включая кликанье, ввод текста) и многих утверждений Playwright требуется обращение к определенному DOM-элементу на странице.
  • Часто один и тот же текст появляется на странице несколько раз. Как человек, взаимодействующий с сайтом, я могу легко отличить кнопку «Delete Team» в диалоге подтверждения от кнопки «Delete Team» на главной странице. Playwright нуждается в большей конкретике для работы с конкретными вариантами использования.
  • Playwright реагирует в таких случаях, используя цепочку селекторов для определения области взаимодействия:
// Поиск диалога.
const dialog = page.getByRole("dialog");
// Ограничение области селектора/клика рамками диалога.
await dialog.getByRole("button", { name: "Delete Team" }).click();

Это не очень хорошо сочетается с языком Gherkin. Мы не смогли найти удобный способ определения области для последующих шагов. На самом деле мы хотели написать что-то вроде этого:

Scenario: Deleting a team
In the confirmation dialog
When I click "Delete Team"
Then something should happen...

Один из способов выполнить это с помощью тестовых контекстов мы нашли в документации Playwright-BDD. Но поскольку он был перегружен шаблонами, не получили того, что хотели. Вместо этого, написали больше селекторов, специфичных для определенных типов взаимодействий. Например, отделили шаг «click a button inside a modal» («нажать на кнопку внутри модала») от «click a button» («нажать на кнопку»).

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

When("I click {string}", async function (this: World, name: string) {
await click(this.page, name);
});

When(
"I click {string} at position {int}",
async function (this: World, name: string, position: number) {
await click(this.page, name, false, position);
}
);

When("I click on {string}", async function (this: World, name: string) {
await click(this.page, name, false);
});

When(
"I click the {string} menu item",
async function (this: World, name: string) {
// слой абстрагирования около `click()`.
await clickMenuItem(this.page, name);
}
);

// еще около сотни таких строк...

Скажите, как разработчик, пишущий тест, вы бы сразу поняли, какой селектор нужно использовать, чтобы щелкнуть по элементу? Я точно не знал, хотя сам написал большинство из них. Эта проблема существовала всегда, просто ее заслоняли более серьезные загвоздки, упомянутые выше. Мы сделали шаг назад и задумались о результатах. Зачем мы писали тесты и что было важно?

Мы стремились получить надежный продукт и написать как можно больше эффективных тестов в максимально сжатые сроки. Мы поняли, что наш фактический набор Gherkin-тестов, хотя и был эффективным, но не являлся конечной целью нашего исследования в области тестирования. Итерации по стеку технологий тестирования при рассмотрении Gherkin-тестов как «непреложного ориентира» были заблуждением с точки зрения невозвратных затратах. Если использование Gherkin принесет нам оптимальный результат — отлично. А если нет? Нам лучше заняться чем-то другим.

Что касается меня, я нашел это «что-то другое» в выступлении Дебби О’Брайен — старшего администратора проектов в Microsoft.

Этап 2: Playwright и IDE

Или как я перестал волноваться и полюбил плагин VS Code

Каждый подход, который мы использовали до сих пор, приводил к «эффективным» тестам. Они гарантировали ожидаемую работу Tempest. Набор тестов запускался и подтверждал тот факт, что мы не вносим регрессии. Однако все эти подходы не обеспечивали должного качества — простоты и скорости — разработки. Переход с Cucumber-js на Playwright-BDD подвел более прочный технический фундамент и значительно оптимизировал работу с тестами, но мало что дал для улучшения процесса написания тестов. Мы сделали ставку на то, что стремление к лучшему опыту разработчиков при написании тестов приведет к оптимальному результату:

  • Запись тестов с помощью кликов по приложению, что в буквальном смысле меняет правила игры.
  • Значительное уменьшение переключений между редактором, пользовательским интерфейсом тестирования и терминалом.
  • Легкий запуск отдельных тестов из редактора, а не путем передачи флагов CLI.

Наш первоначальный опыт работы с UI-режимом Playwright был замечательным, и вся наша команда использует Cursor или VS Code — так что, если мы полностью перейдем на предложенный Playwright метод генерации тестов с помощью расширения VS Code? Мы опробовали этот подход на группе тестов разной сложности и сразу же получили результат на нескольких уровнях:

  1. Создание заглушки тестового файла в Playwright и запуск записи.
  1. Использование Gherkin-теста в качестве руководства для кликанья по приложению.
  1. Сохранение теста и переход к следующему.

После того, как мы переработали поток аутентификации/настройки, процесс переноса оставшихся Gherkin-тестов заключался в следующем:

  • Что касается повторяющихся взаимодействий (например, аутентификация в приложении Tempest GitHub в Recipes), мы абстрагировали их в фикстуры Playwright, но это было легко сделать после написания начальных тестов. 
  • Самым болезненным процессом было начинать с чистого листа и выяснять, какие селекторы нужно составить, чтобы проверить поведение — от этого мы полностью избавились.

Итак, как все прошло?

Довольно хорошо! С точки зрения качества, мы получили положительные отзывы членов команды и обнаружили, что они чаще запускают набор тестов во время разработки для выявления регрессии. С точки зрения количества, они стали писать более исчерпывающие тесты для сопровождения своей работы. Вместо тестирования только идеального сценария (happy path), мы провели больше тестов на граничные случаи. В процессе миграции нашли и исправили несколько ошибок в продукте, что является хорошим знаком.

Есть что-то ироничное в том, что конечный пункт нашего исследования можно было бы назвать так: «Просто сделайте это так, как предлагает Playwright». Но это хорошее напоминание о том, что нужно доверять авторам замечательных проектов в плане понимания того, как лучше использовать их инструменты.

Следующие шаги

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

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

Читайте также:

Читайте нас в Telegram, VK и Дзен


Перевод статьи Eric Skram: How We Built Automated Testing with Playwright

Предыдущая статьяC++: полное руководство по циклам while
Следующая статьяАттестации: новое поколение подписей в PyPI