Аннотации тестов

Урок: Аннотации тестов

Введение

Представь, что у тебя есть список задач на день. Какие-то задачи срочные, какие-то можно отложить, какие-то вообще пока не актуальны. Чтобы не запутаться, ты начинаешь помечать их:

  • «срочно»
  • «позже»
  • «не делать»

Такие пометки помогают быстро ориентироваться и управлять работой.

В тестировании происходит похожая ситуация. Не все тесты одинаковые:

  • какие-то ещё не готовы;
  • какие-то временно падают;
  • какие-то нужно запускать только в определённых условиях.

Чтобы управлять этим, в Playwright используются аннотации тестов.

Аннотации — это специальные пометки, которые изменяют поведение тестов.

Официальный обзор: Annotations | Playwright. Низкоуровневые методы на объекте test: test.skip / fail / fixme / slow / only.


Что такое аннотации тестов

Аннотации — это способы сказать раннеру Playwright Test, как относиться к тесту или к группе.

Встроенные (меняют запуск и таймауты):

  • test.skip — тест не выполняется (помечен как нерелевантный);
  • test.fail — тест должен упасть; если вдруг прошёл — это ошибка прогона;
  • test.fixme — тест помечен как сломанный и не запускается (в отличие от fail, где тест всё же бежит);
  • test.slow — помечает тест как медленный и утраивает таймаут теста;
  • test.only — при наличии хотя бы одного only выполняются только такие тесты в проекте.

Часть аннотаций можно задавать условно (зависят от фикстур вроде browserName, isMobile и т.д.).

Простой пример:

test.skip('этот тест пропущен', async ({ page }) => {
  // код теста
});

Здесь:

  • test.skip — это аннотация;
  • тест не будет выполняться.

Важно: аннотация не меняет код теста, а меняет его поведение.


Пропуск тестов (skip)

Иногда тест временно не нужен или не работает.

test.skip('логин временно отключён', async ({ page }) => {
  await page.goto('/login');
});

Что происходит:

  • тест игнорируется;
  • не запускается вообще.

Это полезно, когда:

  • функциональность в разработке;
  • тест временно падает;
  • нужно исключить тест из прогона.

Условный skip

Условный пропуск задаётся внутри тела теста (или в начале describe): первый аргумент — условие, второй — причина в отчёте.

test('не в Firefox пока', async ({ page, browserName }) => {
  test.skip(browserName === 'firefox', 'Дорабатываем под Firefox');
  await page.goto('/app');
  // ...
});

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

Пропустить всю группу по условию можно так (первой строкой внутри describe):

test.describe('только Chromium', () => {
  test.skip(({ browserName }) => browserName !== 'chromium', 'Нужен Chromium');

  test('один', async ({ page }) => {
    /* ... */
  });
});

См. Conditionally skip a group of tests.


Только этот тест (only)

Иногда нужно запустить один конкретный тест.

test.only('проверка логина', async ({ page }) => {
  await page.goto('/login');
});

Что происходит:

  • в прогоне участвуют только тесты с test.only / test.describe.only (если они есть);
  • остальные тесты не запускаются.

Это удобно при локальной отладке. Важно: не коммитить only — в CI так можно случайно прогнать долю набора и получить «зелёный» ложный результат.


Пометка ожидаемой ошибки (fail)

Иногда ты знаешь, что тест сейчас падает, но это ожидаемо.

test.fail('известный баг', async ({ page }) => {
  await page.goto('/broken-page');
});

Что происходит:

  • если тест упадёт — он будет считаться «успешным»;
  • если неожиданно пройдёт — будет считаться ошибкой.

Это помогает:

  • отслеживать известные баги;
  • понимать, когда проблема исправлена.

fixme — тест не запускается

test.fixme() по смыслу близок к «сломано, чиним»: тест не выполняется, в отличие от test.fail, где сценарий запускается и ожидается падение.

test.fixme('падает на CI из-за тайминга', async ({ page }) => {
  // не бежит, пока не снимешь fixme
});

Условный вариант — внутри теста или хука:

test.beforeEach(async ({ page, isMobile }) => {
  test.fixme(isMobile, 'Настройки на мобильном пока не готовы');
  await page.goto('/settings');
});

См. Use fixme in beforeEach hook.


Медленные тесты (test.slow)

Для отдельно тяжёлых сценариев в теле теста вызывают test.slow() — таймаут теста утраивается (см. test.slow).

test('долгий импорт данных', async ({ page }) => {
  test.slow();
  await page.goto('/reports/heavy');
  // ...
});

Не путать с test.setTimeout в хуках — это разные механизмы; test.slow() нельзя вызывать в beforeAll / afterAll (для хуков используют test.setTimeout внутри хука).


Групповые аннотации через describe

Аннотации можно применять к группе тестов.

test.describe('логин', () => {
  test.skip('тест 1', async () => {});
  test('тест 2', async () => {});
});

Или ко всей группе:

test.describe.skip('логин', () => {
  test('тест 1', async () => {});
  test('тест 2', async () => {});
});

Здесь все тесты внутри блока пропущены. Аналогично существуют test.describe.only, test.describe.fail и др. — см. test.describe.


Теги и фильтрация (--grep)

Теги задают в названии (@slow в строке) или в опциях при объявлении — затем их подхватывает отчёт и CLI.

test('смоук логина', { tag: '@smoke' }, async ({ page }) => {
  // ...
});

test('полный отчёт @slow @vrt', async ({ page }) => {
  // ...
});

test.describe('отчёты', { tag: '@report' }, () => {
  test('заголовок', async ({ page }) => {
    /* ... */
  });
});

Запуск только тестов с тегом:

npx playwright test --grep @smoke

Исключить тег, логическое ИЛИ и И — в Tag tests. В конфиге можно использовать grep / grepInvert на уровне проекта.


Аннотации при объявлении теста

Помимо тегов можно задать тип и описание — они попадают в HTML-отчёт и API репортёра (типы, начинающиеся с _, скрываются в встроенном отчёте).

test('страница входа', {
  annotation: {
    type: 'issue',
    description: 'https://github.com/microsoft/playwright/issues/23180',
  },
}, async ({ page }) => {
  // ...
});

Несколько аннотаций — массивом в annotation: [ … ]. См. Annotate tests.


Метаданные во время прогона (test.info())

Во время выполнения теста можно дописать аннотации в test.info().annotations (например, версию браузера):

test('пример', async ({ page, browser }) => {
  test.info().annotations.push({
    type: 'browser version',
    description: browser.version(),
  });
  // ...
});

См. Runtime annotations.


Пример из реального сценария

test.describe('авторизация', () => {
  test.skip('логин через соцсети', async ({ page }) => {
    // временно отключён
  });

  test('логин через email', async ({ page }) => {
    await page.goto('/login');
    await page.getByLabel('Email').fill('test@mail.com');
    await page.getByLabel('Пароль').fill('1234');
    await page.getByRole('button', { name: 'Войти' }).click();

    await expect(page.getByText('Добро пожаловать')).toBeVisible();
  });
});

Что здесь важно:

  • один тест отключён;
  • другой выполняется;
  • структура понятна.

Почему аннотации важны

Без аннотаций:

  • сложно управлять тестами;
  • приходится удалять или комментировать код;
  • трудно отслеживать состояние тестов.

С аннотациями:

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

Где это используется на практике

Аннотации активно используются:

  • при разработке тестов;
  • при работе с багами;
  • в CI/CD;
  • в больших проектах.

Например:

  • временно отключить тест;
  • пометить известную проблему;
  • запустить только нужный сценарий;
  • адаптировать тесты под окружение.

Итоговое понимание

Аннотации тестов — это способ управлять поведением тестов без размазывания логики по if вокруг всего файла.

Ключевые приёмы из документации Playwright:

  • skip / only / условный skip в теле теста или в describe;
  • fail против fixme (запуск с ожиданием падения vs отключение);
  • slow для увеличения таймаута;
  • теги и --grep для выборочных прогонов;
  • аннотации при объявлении и runtime через test.info().annotations.

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