При работе со сквозным тестированием (E2E, end-to-end) в Playwright критическое значение имеет поддержание упорядоченной и масштабируемой тестовой базы. Хорошо организованная структура не только улучшает поддерживаемость, но и облегчает адаптацию новых коллег. В этой статье мы рассмотрим, как организовать тесты в Playwright наилучшим образом, начиная со структуры папок и заканчивая использованием хуков, аннотаций и тегов.

Структурирование папок с тестами

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

/tests
  /helpers
    - list-test.ts        # кастомная фикстура для страницы с списком фильмов
    - list-utilities.ts       # функции хелперы для создания списков фильмов
  /logged-in
    - api.spec.ts    # API тесты для пользователей, вошедших в систему
    - login.setup.ts      # Тесты для входа в систему
    - manage-lists.spec.ts  # Тесты для управления списками фильмов
  /logged-out
    - api.spec.ts # Тесты API для пользователей, не вошедших в систему
    - auth.spec.ts     # Тесты потока аутентификации
    - movie-search.spec.ts  # Тесты для поиска фильмов
    - sort-by.spec.ts       # Тесты для сортировки фильмов

Тестовые хуки в Playwright

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

test.beforeEach(async ({ page }) => {
  await page.goto('');
});

test('должен отредактировать существующий список', async ({ page }) => {
  // ...
});

test('должен добавлять и удалять фильмы из списка', async ({ page }) => {
  // ...
});

Когда подготовка и очистка выполняются для нескольких тестов, функции хелперы (или вспомогательные функции) помогут избежать дублирования кода и поддержать принцип DRY . Ниже приведен пример вспомогательных функций для создания списка фильмов:

import { test, expect, Page } from '@playwright/test';

export async function createList(
  page: Page,
  listName: string,
  listDescription: string,
) {
  await test.step('создать новый список', async () => {
    await page.getByLabel('Профиль пользователя').click();
    await page.getByRole('link', { name: 'Создать новый список' }).click();
    await page.getByLabel('Имя').fill(listName);
    await page.getByLabel('Описание').fill(listDescription);
    await page.getByRole('button', { name: 'Продолжить' }).click();
  });
}

export async function openLists(page: Page, name: string = 'Мои списки') {
  //...
}

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

import { test, expect } from '@playwright/test';
import { addMovie, createList, openLists } from '../helpers/list-utilities';

// Перед каждым тестом переходим на базовый URL, создаем список и открываем страницу со списками
test.beforeEach(async ({ page }) => {
  await page.goto('');
  await createList(
    page,
    'мои любимые фильмы',
    'вот список моих любимых фильмов',
  );
  await openLists(page);
});

test('должен отредактировать существующий список', async ({ page }) => {
  await page.getByRole('link', { name: 'мои любимые фильмы' }).click();
  await page.getByRole('link', { name: 'Редактировать' }).click();
  // ...
});

test('должен добавлять и удалять фильмы из списка', async ({ page }) => {
  await page.getByRole('link', { name: 'мои любимые фильмы' }).click();
  await page.getByRole('button', { name: 'Добавить/Удалить фильмы' }).click();
  //...
});

Фикстуры

Фикстуры могут быть использованы вместо хука beforeEach и являются отличным способом создания контекста страницы, который можно использовать в нескольких тестах. Фикстуры можно определить в отдельном файле и импортировать в тестовые файлы, где они необходимы. Пример кастомной фикстуры для страницы с фильмами:

export const listTest = baseTest.extend<{ listPage: Page }>({
  listPage: async ({ context }, use) => {
    // настройка фикстуры
    const page = await context.newPage();
    await page.goto('');
    await createList(page, 'мои любимые фильмы', 'список моих любимых фильмов');

    await listTest.step('добавить фильмы в список', async () => {
      await addMovie(page, 'Twisters');
      await addMovie(page, 'The Garfield Movie');
      await addMovie(page, 'Bad Boys: Ride or Die');
    });
  //...
});

Фикстура может использоваться в тестах, импортируя ее и передавая как аргумент в функцию test, вместо использования встроенной фикстуры page Playwright. Каждый тест, использующий кастомную фикстуру, начнётся с уже подготовленной страницы, на которой есть список фильмов:

import { expect } from '@playwright/test';
import { listTest as test } from '../helpers/list-test';
import { addMovie } from '../helpers/list-utilities';

test('редактирование существующего списка', async ({ listPage }) => {
  // Используем страницу из фикстуры listPage
  const page = listPage;

  await page.getByRole('link', { name: 'Редактировать' }).click();
  // ...
});

Разбиение тестов на шаги с помощью test.step

Если вы хотите сделать тесты более понятными, полезной окажется функция test.step в Playwright. Она разбивает сложные тесты на более понятные короткие шаги, что повышает читаемость и улучшает отчётность.

import { test, expect } from '@playwright/test';

test('должен добавлять и удалять фильмы из списка', async ({ page }) => {
  const movieList = page.getByRole('listitem', { name: 'movie' });
  await page.getByRole('link', { name: 'мои любимые фильмы' }).click();
  await page.getByRole('button', { name: 'Добавить/Удалить фильмы' }).click();

  await test.step('добавить фильмы в список и проверить их', async () => {
    await addMovie(page, 'Twisters');
    await addMovie(page, 'Bad Boys: Ride or Die');
    await expect
      .soft(movieList)
      .toHaveText([/Twisters/, /Bad Boys: Ride or Die/]);
  });

  await test.step('удалить фильмы из списка и проверить изменения', async () => {
    const movie1 = page.getByRole('listitem').filter({ hasText: 'Twisters' });
    await movie1.getByLabel('Удалить').click();
    await expect.soft(movieList).toHaveText([/Bad Boys: Ride or Die/]);
  });
});

Использование встроенных аннотаций: skip, fail и fixme

Аннотации в Playwright помогают маркировать тесты в зависимости от их поведения или условий выполнения, например, пропускать тесты в зависимости от определённых условий, помечать как ожидаемо неудачные или помечать флагом как требующие внимания. Давайте рассмотрим каждую из них с примерами:

1. test.skip: Условный пропуск тестов

Аннотация test.skip полезна, когда необходимо пропустить тест в зависимости от определённых условий или конфигурации. Например, вы можете захотеть пропустить тесты для конкретных браузеров, в определённых окружениях или когда недоступна какая-либо функция.

import { test, expect } from '@playwright/test';

test('гамбургер-меню на мобильных устройствах', async ({ page, isMobile }) => {
  // Пропустить тест на мобильных устройствах
  test.skip(!isMobile, 'Тест актуален только для десктопа');

  await page.goto('/');
  //..
});

test('не должно работать на Safari', async ({ page, browserName }) => {
  // Пропустить тест на Safari из-за известных проблем с совместимостью
  test.skip(browserName === 'webkit', 'Safari не поддерживает эту функцию');

  await page.goto('');
  //..
});

2. test.fixme: Пометка тестов для исправления

Используйте test.fixme для пометки тестов, которые нужно исправить позже. Эта аннотация используется для пометки неполных или неправильно реализованных тестов. Playwright автоматически пропустит такие тесты и отразит их в отчётах, что послужит напоминанием о необходимости их доработки.

import { test, expect } from '@playwright/test';

// Помечаем этот тест как "fixme", так как он еще не полностью реализован
test.fixme('войти в систему и проверить доступ к профилю', async ({ page }) => {
  await page.goto('');
  await page.getByRole('banner').getByLabel('Войти').click();
  await page
    .getByPlaceholder('you@example.com')
    .fill(process.env.MOVIES_USERNAME!);
  await page.getByPlaceholder('Пароль').fill(process.env.MOVIES_PASSWORD!);
  await page.getByRole('button', { name: 'Войти' }).click();
  await page.getByLabel('Профиль пользователя').click();
});

Как эти аннотации упрощают тестирование

  • test.skip — удобный способ условного пропуска тестов в зависимости от критериев, таких как окружение, платформа или доступность функции. Это помогает поддерживать «зелёный» тестовый набор, пропуская нерелевантные тесты, а не позволяя им падать.

  • test.fixme полезен для пометки незавершённых или не реализованных тестов. Эти тесты пропускаются автоматически и помечаются в отчётах, напоминая о необходимости их доработки в будущем.

Резюме использования аннотаций

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

Добавление пользовательских аннотаций

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

test(
  'должен удалить список',
  {
    annotation: {
      type: 'issue',
      description: 'https://github.com/microsoft/demo.playwright.dev/issues/58',
    },
  },
  async ({ page }) => {
    await page.getByRole('link', { name: 'мои любимые фильмы' }).click();
    await page.getByRole('link', { name: 'Редактировать' }).click();
    await page.getByRole('link', { name: 'Удалить список' }).click();
  // ... оставшиеся шаги теста
});

Использование тегов для фильтрации и организации тестов

Теги — мощный инструмент для организации и фильтрации тестов. Вы можете использовать теги для организации тестов по функциям, приоритетам, ролям пользователей или циклам выпуска. Это позволяет легко фильтровать и запускать нужные тесты или генерировать целевые отчёты.

Как добавлять теги к тестам

В Playwright вы можете добавлять теги как часть метаданных теста. Вот пример того, как можно определить теги для тестов:

test('сортировка с мокированием API', { tag: '@mocking' }, async ({ page }) => {
  // Мокируем вызов API для сортировки по популярности
  await page.route('*/**/**sort_by=popularity.desc', async (route) => {
    await route.fulfill({
      path: path.join(__dirname, '../mocks/sort-by-popularity.json'),
    });
  });
});

В этом примере мы пометили тест тегом @mocking. Эта метаинформация будет использоваться для фильтрации тестов в отчётах или при выполнении командной строки.

Запуск тестов с фильтрацией по тегам

Чтобы запустить тесты с конкретными тегами, можно использовать флаг --grep с указанием имени тега:

npx playwright test --grep @mocking

Чтобы исключить тесты с конкретным тегом, используйте флаг --grep-invert:

npx playwright test --grep-invert @mocking

Фильтрация по тегам в HTML-отчётах

Теги отображаются в HTML-отчёте, что облегчает идентификацию и фильтрацию тестов по тегам при просмотре результатов. Это может быть очень полезно для отладки или фокусировки на подмножестве тестов, связанных с определённой функцией.

Резюме

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

  • Организованная структура папок. Разделяйте тесты по их контексту (вошёл  / не вошёл в систему) и по функциональности. Например, при тестировании GitHub у нас могут быть папки tests/repos, tests/prs, tests/issues и так далее.

  • Использование хуков и блоков Describe. Улучшайте читаемость и создавайте общие предварительные условия.

  • Определение шагов. Используйте test.step, чтобы разбить сложные тест-кейсы на более понятные шаги.

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

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


Может быть, вы давно задумываетесь о переходе в QA, но не знаете, с чего начать? Или, возможно, вам нужно освоить новые инструменты, чтобы улучшить процесс тестирования в вашем проекте? В любом случае рекомндуем обратить внимание на открытые уроки:

Если у вас уже есть опыт в тестировании и вы хотите двигаться дальше к управлению QA — пройдите вступительный тест, чтобы оценить свой уровень и узнать, подойдет ли вам программа курса "QA Lead".

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