При работе со сквозным тестированием (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, но не знаете, с чего начать? Или, возможно, вам нужно освоить новые инструменты, чтобы улучшить процесс тестирования в вашем проекте? В любом случае рекомндуем обратить внимание на открытые уроки:
15 июля в 20:00 — Вы уже тестировщик. Просто не знали об этом
Как превратить ежедневные навыки в востребованную IT-профессию.23 июля в 20:00 — Тестирование React-приложений с помощью React Testing Library
Как писать надежные тесты для компонентов React без лишних деталей реализации.19 августа в 19:00 — Docker Compose для тестировщика: легко о сложном
Настройка тестовых сред с помощью Docker Compose и ускорение процесса автоматизации.
Если у вас уже есть опыт в тестировании и вы хотите двигаться дальше к управлению QA — пройдите вступительный тест, чтобы оценить свой уровень и узнать, подойдет ли вам программа курса "QA Lead".