Playwright: гайд по организации тестов

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

Папки

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

/tests
  /helpers
    - list-test.ts        # custom fixture for a page with list of movies
    - list-utilities.ts       # helper functions for creating lists of movies
  /logged-in
    - api.spec.ts    # API tests for logged-in users
    - login.setup.ts      # Tests for logging in
    - manage-lists.spec.ts  # Tests for managing lists of movies
  /logged-out
    - api.spec.ts # Tests API endpoints for logged-out users
    - auth.spec.ts     # Tests login flow
    - movie-search.spec.ts  # Tests for searching movies
    - sort-by.spec.ts       # Tests for sorting movies

Хуки

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

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

test('should edit an existing list', async ({ page }) => {
  // ...
});

test('should add and delete movies from a list', async ({ page }) => {
  //...
});

Когда у вас есть задачи по настройке и очистке нескольких тестов, эти вспомогательные функции помогут вам избежать дублирования кода и обслуживать ваши тесты с соблюдением принципа DRY (Don’t Repeat Yourself)

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

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

export async function createList(
  page: Page,
  listName: string,
  listDescription: string,
) {
  await test.step('create a new list', async () => {
    await page.getByLabel('User Profile').click();
    await page.getByRole('link', { name: 'Create New List' }).click();
    await page.getByLabel('Name').fill(listName);
    await page.getByLabel('Description').fill(listDescription);
    await page.getByRole('button', { name: 'Continue' }).click();
  });
}

export async function openLists(page: Page, name: string = 'My Lists') {
  //...
}

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

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

// Before each test, navigate to the base URL, create a list, and open the lists page
test.beforeEach(async ({ page }) => {
  await page.goto('');
  await createList(
    page,
    'my favorite movies',
    'here is a list of my favorite movies',
  );
  await openLists(page);
});

test('should edit an existing list', async ({ page }) => {
  await page.getByRole('link', { name: 'my favorite movies' }).click();
  await page.getByRole('link', { name: 'Edit' }).click();
  // ...
});

test('should add and delete movies from a list', async ({ page }) => {
  await page.getByRole('link', { name: 'my favorite movies' }).click();
  await page.getByRole('button', { name: 'Add/Remove Movies' }).click();
  //...
});

Фикстуры

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

Вот пример фикстуры для страницы со списком фильмов:

export const listTest = baseTest.extend<{ listPage: Page }>({
  listPage: async ({ context }, use) => {
    // fixture setup
    const page = await context.newPage();
    await page.goto('');
    await createList(page, 'my favorite movies', 'list of my favorite movies');

    await listTest.step('add movies to list', async () => {
      await addMovie(page, 'Twisters');
      await addMovie(page, 'The Garfield Movie');
      await addMovie(page, 'Bad Boys: Ride or Die');
    });
  //...
});

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

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

test('editing an existing list', async ({ listPage }) => {
  // set the page to the listPage fixture
  const page = listPage;

  await page.getByRole('link', { name: 'Edit' }).click();
  // ...
});

Разделение тестов на шаги: test.step 

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

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

test('should add and delete movies from a list', async ({ page }) => {
  const movieList = page.getByRole('listitem', { name: 'movie' });
  await page.getByRole('link', { name: 'my favorite movies' }).click();
  await page.getByRole('button', { name: 'Add/Remove Movies' }).click();

  await test.step('add and verify movies in the list', 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('remove and verify movies in the list', async () => {
    const movie1 = page.getByRole('listitem').filter({ hasText: 'Twisters' });
    await movie1.getByLabel('Remove').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('hamburger menu in mobile', async ({ page, isMobile }) => {
  // Skip the test if viewport is mobile
  test.skip(!isMobile, 'Test is only relevant for desktop');

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

test('should not run on Safari', async ({ page, browserName }) => {
  // Skip the test on Safari due to known compatibility issues
  test.skip(browserName === 'webkit', 'Safari not supported for this feature');

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

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

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

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

// Mark this test as a "fixme" since it's not fully implemented yet
test.fixme('log user in and verify profile access', async ({ page }) => {
  await page.goto('');
  await page.getByRole('banner').getByLabel('Log In').click();
  await page
    .getByPlaceholder('you@example.com')
    .fill(process.env.MOVIES_USERNAME!);
  await page.getByPlaceholder('Password').fill(process.env.MOVIES_PASSWORD!);
  await page.getByRole('button', { name: 'login' }).click();
  await page.getByLabel('User Profile').click();
});

Как эти аннотации приносят пользу:

  • test.skip отлично подходит для запуска тестов по условию, на основе таких критериев, как тестовое окружение, платформа или доступность фичи. Это помогает поддерживать «зеленый» набор тестов, пропуская не готовые тесты. 
  • test.fixme полезна, когда у вас есть незавершенные или еще не реализованные фичи. Такие тесты автоматически пропускаются, и о них сообщается в репорте, что позволяет перенести их в будущие задачи. 

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

Добавление кастомных аннотаций 

В Playwright можно добавлять собственные (кастомные) аннотации к тестам. Например, часто используется ссылка на связанную issue. Они могут быть полезны в репортах, а также видны в UI-режиме. 

Пример, как добавить ссылки на issues в тесты:

test(
  'should delete a list',
  {
    annotation: {
      type: 'issue',
      description: 'https://github.com/microsoft/demo.playwright.dev/issues/58',
    },
  },
  async ({ page }) => {
    await page.getByRole('link', { name: 'my favorite movies' }).click();
    await page.getByRole('link', { name: 'Edit' }).click();
    await page.getByRole('link', { name: 'Delete List' }).click();
  // ... remaining test steps
});

Теги 

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

Добавление тегов 

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

test('sort by with api mocking', { tag: '@mocking' }, async ({ page }) => {
  // Mock the API call for sorting by popularity
  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-репорте, что позволяет легче идентифицировать и фильтровать тесты по тегам, при просмотре результатов. Это может быть полезно для отладки или концентрации внимания на подмножестве тестов, связанных с определенной фичей.

Главное

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

  • Упорядоченная структура папок: Разделяйте тесты в зависимости от их контекста, и по фичам. Например, при тестировании GitHub у вас должны быть папки tests/repos, tests/prs, tests/issues и т. д. 
  • Хуки и блоки Describe: Улучшение читабельности и создание предварительных условий для запуска.
  • Разбивка на шаги: Используйте test.step для разбивки сложных тест-кейсов. 
  • Аннотации и теги: Используйте аннотации и теги, чтобы отмечать упавшие или недописанные тесты, и связывать их с issues.

Debbie O’Brien


Какой была ваша первая зарплата в QA и как вы искали первую работу?

Мега обсуждение в нашем телеграм-канале о поиске первой работы. Обмен опытом и мнения.

Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии

Мы в Telegram

Наш официальный канал
Полезные материалы и тесты
Готовимся к собеседованию
Project- и Product-менеджмент

? Популярное

? Telegram-обсуждения

Наши подписчики обсуждают, как искали первую работу в QA. Некоторые ищут ее прямо сейчас.
Наши подписчики рассказывают о том, как не бояться задавать тупые вопросы и чувствовать себя уверенно в новой команде.
Обсуждаем, куда лучше податься - в менеджмент или по технической ветке?
Говорим о конфликтных ситуациях в команде и о том, как их избежать
$1100*
медианная зарплата в QA в июне 2023

*по результатам опроса QA-инженеров в нашем телеграм-канале

Собеседование

19%*
IT-специалистов переехало или приняло решение о переезде из России по состоянию на конец марта 2022

*по результатам опроса в нашем телеграм-канале

live

Обсуждают сейчас