В текущих проектах я применяю подход Behavior Driven Development (BDD) для написания end‑to‑end тестов. Хотя раньше я скептически относился к Given‑When‑Then синтаксису, теперь часто его использую. Главная причина — я больше не пишу BDD‑сценарии вручную, а генерирую их с помощью ChatGPT.

В статье расскажу, как вы можете генерировать AI-тесты в своем проекте и запускать их в реальном браузере с помощью Playwright.

AI пишет тесты
AI пишет тесты

Для примера я буду использовать демо‑приложение TodoList, разработанное командой Playwright. Это веб‑страница со списком, в который можно добавлять записи, помечать их выполненными и фильтровать по статусу. Конечная цель — покрыть это приложение end‑to‑end тестами, при этом написать минимум кода вручную и делегировать максимум работы ChatGPT. В идеале тесты запускаются и проходят вообще без дополнительных правок. Ниже проверим, насколько это возможно.

TodoList
TodoList

План

А мой список дел для статьи следующий:

  1. Сформулировать требования (user story)

  2. Сгенерировать описания шагов

  3. Сгенерировать BDD сценарии

  4. Запустить тесты

  5. Подвести итог

1. Сформулировать требования (user story)

Пользовательская история (user story) — важнейший элемент BDD. Это описание действий пользователя для достижения конкретной цели в приложении. Для тестирования TodoList возьмём следующие требования:

As a user I want to manage items in todo list: 
create items, complete items and filter to see only completed items.

Все тексты, относящиеся к запросам в ChatGPT, будут на английском прим. переводчика.

Дальше я буду использовать этот текст в запросах к ChatGPT.

2. Сгенерировать описания шагов

Описание шага (step definition) — это конкретная реализация действия пользователя. Например:

Given I open TodoApp page

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

await page.goto('https://demo.playwright.dev/todomvc/');

Или

When I click the button

становится:

await page.getByTestId('button').click();

Набор описаний шагов можно объединить в Page Object Model — класс, соответствующий определенной странице приложения в тестовых сценариях.

Для списка дел потребуется один класс TodoPage с методами для создания, маркировки и фильтрации записей. Сложность в том, что реализация шагов сильно зависит от структуры HTML, поэтому я не буду делегировать это искусственному интеллекту. Но... ChatGPT отлично справится с генерацией шаблона для такого класса.

На выходе я хочу получить код на TypeScript с использованием пакетов @playwright/test и playwright-bdd — чтобы потом запустить эти тесты на Playwright.

Запрос к ChatGPT для генерации шаблона TodoPage :

Create Page Object Model 'TodoPage' in Typescript
for the following feature:

As a user I want to manage items in todo list: 
create items, complete items and filter to see only completed items.

* Use page from '@playwright/test' as constructor parameter.
* Use Given, When, Then from 'playwright-bdd/decorators' as BDD decorators, for example: @Given('pattern {string}').
* Don't fill methods body.

Отправляю запрос в чат и жду результат. Меня всегда немного гипнотизирует процесс авто-генерации кода ????

Generate
Генерация Page Object Model с помощью ChatGPT
Полный код из ответа ChatGPT
import { Page } from '@playwright/test';
import { Given, When, Then } from 'playwright-bdd/decorators';

export class TodoPage {
  private page: Page;

  constructor(page: Page) {
    this.page = page;
  }

  @Given('I am on the Todo page')
  async navigateToTodoPage() {
    // Implement navigation to the Todo page here
  }

  @When('I create a new todo item with text {string}')
  async createTodoItem(text: string) {
    // Implement creating a new todo item here
  }

  @When('I complete the todo item with text {string}')
  async completeTodoItem(text: string) {
    // Implement completing a todo item here
  }

  @When('I filter to see only completed items')
  async filterCompletedItems() {
    // Implement filtering completed items here
  }

  @Then('I should see the todo item with text {string}')
  async verifyTodoItemVisible(text: string) {
    // Implement verification logic here
  }

  @Then('I should not see the todo item with text {string}')
  async verifyTodoItemNotVisible(text: string) {
    // Implement verification logic here
  }

  @Then('I should see only completed todo items')
  async verifyOnlyCompletedItemsVisible() {
    // Implement verification logic here
  }

  @Then('I should see all todo items')
  async verifyAllItemsVisible() {
    // Implement verification logic here
  }
}

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

Теперь я хочу улучшить результат. Вместо 4-х методов верификации оставить один, который сверяет список целиком. Это удобнее, чем проверять записи по одной. Отправляю запрос в чат обычным текстом и получаю исправленный ответ.

Уточняющий запрос:

Replace all verify methods with single method
that verifies list of visible items
Ответ ChatGPT (обратите внимание на последний метод)
import { Page } from '@playwright/test';
import { Given, When, Then } from 'playwright-bdd/decorators';

export class TodoPage {
  private page: Page;

  constructor(page: Page) {
    this.page = page;
  }

  @Given('I am on the Todo page')
  async navigateToTodoPage() {
    // Implement navigation to the Todo page here
  }

  @When('I create a new todo item with text {string}')
  async createTodoItem(text: string) {
    // Implement creating a new todo item here
  }

  @When('I complete the todo item with text {string}')
  async completeTodoItem(text: string) {
    // Implement completing a todo item here
  }

  @When('I filter to see only completed items')
  async filterCompletedItems() {
    // Implement filtering completed items here
  }

  @Then('I should see the following todo items:')
  async verifyTodoItemsVisible(itemList: string[]) {
    for (const itemText of itemList) {
      // Implement verification logic for each itemText here
    }
  }
}

Все исправлено. ChatGPT заменил часть методов, а остальные оставил нетронутыми.

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

  • make all methods async — иногда ChatGPT генерирует синхронные методы

  • use {string} for string pattern parameters — использовать корректный синтакс Cucumber Expression для параметров

  • create todo items inside scenario "xxx" — исправить сценарии, которые используют данные других сценариев, тесты должны быть изолированы

  • don't start method names with given/when/then — для более лаконичных имён методов

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

  1. получить первичный ответ

  2. улучшить его дополнительными командами

  3. финально подправить вручную

Для примера с генерацией TodoPage это особенно релевантно, т.к. в любом случае требуется дописать реализацию методов.

2.1 Реализация методов

Реализацию методов можно писать вручную, изучая HTML-код страницы через инструменты разработчика. Но изначальная цель — сгенерировать максимум кода автоматически. К счастью, Playwright предоставляет инструмент codegen, который записывает все действия на странице в виде команд для браузера.

Режим codegen запускается через терминал:

npx playwright codegen https://demo.playwright.dev/todomvc

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

  1. добавить пару записей

  2. пометить выполненными

  3. отфильтровать

steps in browser
Заполнение списка дел под присмотром codegen

Сгенерированный код:

test('test', async ({ page }) => {
  await page.goto('https://demo.playwright.dev/todomvc/#/');
  await page.getByPlaceholder('What needs to be done?').fill('feed the dog');
  await page.getByPlaceholder('What needs to be done?').press('Enter');
  await page.getByPlaceholder('What needs to be done?').fill('feed the cat');
  await page.getByPlaceholder('What needs to be done?').press('Enter');
  await page.locator('li').filter({ hasText: 'feed the cat' }).getByLabel('Toggle Todo').check();
  await page.getByRole('link', { name: 'Completed' }).click();
});

Из этого кода я выбираю нужные фрагменты и вставляю их в шаблон от ChatGPT.

В некоторых местах ручных правок не избежать. Например, получение DOM элементов через селектор page.locator('li') я заменю на более надежный page.getByTestId('todo-title').

Финальный класс TodoPage со всеми доработками:

TodoPage.ts
import { Page, expect } from '@playwright/test';
import { Given, When, Then, Fixture } from 'playwright-bdd/decorators';
import { DataTable } from '@cucumber/cucumber';

export @Fixture('todoPage') class TodoPage {
  private page: Page;

  constructor(page: Page) {
    this.page = page;
  }

  @Given('I am on the Todo page')
  async navigateToTodoPage() {
    await this.page.goto('https://demo.playwright.dev/todomvc/#/');
  }

  @When('I create a new todo item with text {string}')
  async createTodoItem(text: string) {
    await this.page.getByPlaceholder('What needs to be done?').fill(text);
    await this.page.getByPlaceholder('What needs to be done?').press('Enter');
  }

  @When('I complete the todo item with text {string}')
  async completeTodoItem(text: string) {
    await this.page.getByTestId('todo-item').filter({ hasText: text }).getByLabel('Toggle Todo').check();
  }

  @When('I filter to see only completed items')
  async filterCompletedItems() {
    await this.page.getByRole('link', { name: 'Completed' }).click();
  }

  @Then('I should see the following todo items:')
  async verifyTodoItemsVisible(itemList: DataTable) {
    await expect(this.page.getByTestId('todo-title')).toHaveText(itemList.raw().flat());
  }
}

3. Сгенерировать BDD сценарии

Теперь всё готово для генерации BDD сценариев из созданных шагов. Важно указать ChatGPT использовать только конкретные шаги, чтобы в последующем запускать полученные сценарии без дополнительных правок.

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

Для получения списка доступных шагов в playwright-bdd есть специальная команда bddgen export. Она собирает описания шагов из всех файлов и выводит их в текстовом виде.

Запуск команды и вывод списка шагов из TodoPage:

> npx bddgen export

List of all steps found by config: playwright.config.ts

* Given I am on the Todo page
* When I create a new todo item with text {string}
* When I complete the todo item with text {string}
* When I filter to see only completed items
* Then I should see the following todo items:

Полученный список шагов я вставляю в запрос к ChatGPT.

Запрос для генерации BDD сценариев:

Generate BDD scenarios as a single Gherkin file strictly using
only the following steps:

* Given I am on the Todo page
* When I create a new todo item with text {string}
* When I complete the todo item with text {string}
* When I filter to see only completed items
* Then I should see the following todo items:
Ответ ChatGPT
Feature: Todo List Management

  Scenario: Create a new todo item
    Given I am on the Todo page
    When I create a new todo item with text "Buy groceries"
    Then I should see the following todo items:
      | Todo Text       |
      | Buy groceries   |

  Scenario: Complete a todo item
    Given I am on the Todo page
    When I create a new todo item with text "Read a book"
    And I complete the todo item with text "Read a book"
    Then I should see the following todo items:
      | Todo Text     |
      | [x] Read a book |

  Scenario: Filter completed items
    Given I am on the Todo page
    When I create a new todo item with text "Walk the dog"
    And I create a new todo item with text "Water the plants"
    And I complete the todo item with text "Walk the dog"
    And I filter to see only completed items
    Then I should see the following todo items:
      | Todo Text         |
      | [x] Walk the dog |

  Scenario: Filter completed items with multiple completed items
    Given I am on the Todo page
    When I create a new todo item with text "Go for a run"
    And I create a new todo item with text "Write code"
    And I complete the todo item with text "Go for a run"
    And I complete the todo item with text "Write code"
    And I filter to see only completed items
    Then I should see the following todo items:
      | Todo Text        |
      | [x] Go for a run |
      | [x] Write code   |

Результат снова неплох. ChatGPT корректно использует Given-When-Then синтаксис, генерирует осмысленные сценарии и применяет только указанные шаги.

Как и в предыдущий раз, можно улучшить ответ:

  1. Использовать ключевое слово Background для повторяющихся действий — это аналог beforeEach()

  2. Удалить [x] из названий в таблицах — иначе сравнение записей будет выдавать ошибку

  3. Удалить заголовок Todo Text в таблицах — он тут лишний, т. к. всего одна колонка

Уточняющий запрос:

* Use "Background" for common steps.
* Don't use "[x]"
* Don't use "Todo Text" header
Обновленный ответ ChatGPT
Feature: Todo List Management

  Background:
    Given I am on the Todo page

  Scenario: Create a new todo item
    When I create a new todo item with text "Buy groceries"
    Then I should see the following todo items:
      | Buy groceries   |

  Scenario: Complete a todo item
    When I create a new todo item with text "Read a book"
    And I complete the todo item with text "Read a book"
    Then I should see the following todo items:
      | Read a book   |

  Scenario: Filter completed items
    When I create a new todo item with text "Walk the dog"
    And I create a new todo item with text "Water the plants"
    And I complete the todo item with text "Walk the dog"
    And I filter to see only completed items
    Then I should see the following todo items:
      | Walk the dog   |

  Scenario: Filter completed items with multiple completed items
    When I create a new todo item with text "Go for a run"
    And I create a new todo item with text "Write code"
    And I complete the todo item with text "Go for a run"
    And I complete the todo item with text "Write code"
    And I filter to see only completed items
    Then I should see the following todo items:
      | Go for a run   |
      | Write code     |

Необходимые правки внесены. Настал момент запустить это в браузере!

4. Запустить тесты

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

Для краткости я опущу установку зависимостей. Вы можете посмотреть готовый репозиторий на GitHub со всеми файлами из статьи. Попробуйте сгенерировать свои BDD сценарии для TodoList и запустить их на шагах из примера.

Конфигурация для Playwright следующая:

playwright.config.ts
import { defineConfig } from '@playwright/test';
import { defineBddConfig } from 'playwright-bdd';

const testDir = defineBddConfig({
  paths: ['./features/todopage.feature'], // <- BDD сценарии
  importTestFrom: 'steps/fixtures.ts',    // <- описания шагов
});

export default defineConfig({
  testDir,
  reporter: 'html',
});

Файл fixtures.ts подключает созданный ранее TodoPage в виде фикстуры:

steps/fixtures.ts
import { test as base } from 'playwright-bdd';
import { TodoPage } from './TodoPage';

export const test = base.extend<{ todoPage: TodoPage }>({
  todoPage: async ({ page }, use) => use(new TodoPage(page)),
});

Запускаем тесты:

npx bddgen && npx playwright test

Результат:

Running 4 tests using 1 worker
  4 passed (2.7s)

To open last HTML report run:

  npx playwright show-report

Все тесты прошли! Ура ????

HTML отчёт содержит детали по каждому сценарию:

HTML report
HTML отчёт

5. Подвести итог

Я доволен полученным результатом. Да, пришлось корректировать ответы ChatGPT, но на это ушло гораздо меньше времени, чем если бы я писал этот код вручную. Мне нравится такой способ написания тестов — не тот монотонный процесс, про который в некоторых командах иногда забивают забывают. Это современный вариант парного программирования, когда твой партнёр — нейросеть. Она не идеальная и может ошибаться, зато очень исполнительная, невероятно начитанная и готова с энтузиазмом браться за любые задачи!

BDD-формат отлично подходит для AI-генерации. Он понятный и легко читаемый. Нейросеть собирает шаги в логически правильные цепочки, а человек быстро валидирует результат и находит ошибки. При этом формат технический, сценарии можно сразу запускать в реальном браузере, используя Playwright и другие инструменты.

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

Благодарю за внимание и зелёных вам тестов ❤️

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


  1. kentastik
    21.09.2023 04:38

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