Кастомизация assertions

Урок: Кастомизация assertions

Введение

Когда ты только начинаешь писать тесты, утверждения (expect) выглядят просто и понятно: проверил текст, убедился, что элемент видим — и всё работает.

Но по мере роста проекта возникает проблема. Проверки начинают повторяться. Например:

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

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

Хочется сказать: «просто проверь, что пользователь авторизован».

Именно для этого нужна кастомизация assertions — возможность создавать свои проверки и настраивать поведение стандартных.

Это делает тесты:

  • короче;
  • понятнее;
  • более поддерживаемыми.

Зачем кастомизировать assertions

Рассмотрим повторяющийся код:

await expect(page.getByText('Добро пожаловать')).toBeVisible();
await expect(page.getByTestId('avatar')).toBeVisible();
await expect(page.getByRole('button', { name: 'Выйти' })).toBeVisible();

Такая проверка может встречаться в десятках тестов.

Проблема:

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

Хочется заменить это на что-то вроде:

await expectUserLoggedIn(page);

Это и есть идея кастомных assertions.


Настройка стандартных assertions

Первый уровень кастомизации — это настройка поведения expect.

Например, можно изменить таймаут:

await expect(page.getByText('Успешно')).toBeVisible({ timeout: 10000 });

Здесь:

  • мы увеличили время ожидания до 10 секунд.

Это полезно, если:

  • данные грузятся долго;
  • есть анимации;
  • нестабильное окружение.

Также можно использовать глобальные настройки:

test.use({
  expect: {
    timeout: 10000,
  },
});

Теперь все expect будут ждать дольше.


Использование expect с опциями

Можно добавлять дополнительные параметры:

await expect(page.getByText('Ошибка')).toBeVisible({
  timeout: 5000,
});

Или проверять частичное совпадение текста:

await expect(page.getByRole('heading')).toContainText('Добро');

Здесь:

  • не обязательно точное совпадение;
  • проверяется наличие части текста.

Создание собственных функций-assertions

Самый простой способ кастомизации — вынести проверки в функцию.

async function expectUserLoggedIn(page) {
  await expect(page.getByText('Добро пожаловать')).toBeVisible();
  await expect(page.getByTestId('avatar')).toBeVisible();
}

Использование:

await expectUserLoggedIn(page);

Что это даёт:

  • код становится короче;
  • логика централизована;
  • изменения делаются в одном месте.

Добавление параметров в кастомные проверки

Можно сделать проверки более гибкими:

async function expectUserName(page, name) {
  await expect(page.getByTestId('username')).toHaveText(name);
}

Использование:

await expectUserName(page, 'Stepan');

Теперь функция работает для разных данных.


Кастомные assertions через expect.extend

Playwright (через expect из Jest) позволяет расширять assertions.

Пример:

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

expect.extend({
  async toBeLoggedIn(page) {
    const locator = page.getByTestId('avatar');
    const isVisible = await locator.isVisible();

    if (isVisible) {
      return {
        pass: true,
        message: () => 'Пользователь залогинен',
      };
    } else {
      return {
        pass: false,
        message: () => 'Пользователь не залогинен',
      };
    }
  },
});

Использование:

await expect(page).toBeLoggedIn();

Разберём:

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

Когда стоит использовать extend, а когда функции

Есть два подхода:

Функции:

await expectUserLoggedIn(page);

Подходят, когда:

  • нужна простая логика;
  • не требуется интеграция в expect.

expect.extend:

await expect(page).toBeLoggedIn();

Подходит, когда:

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

Улучшение читаемости тестов

Сравним два варианта.

Без кастомизации:

await expect(page.getByText('Добро пожаловать')).toBeVisible();
await expect(page.getByTestId('avatar')).toBeVisible();
await expect(page.getByRole('button', { name: 'Выйти' })).toBeVisible();

С кастомизацией:

await expectUserLoggedIn(page);

Или:

await expect(page).toBeLoggedIn();

Второй вариант:

  • проще читается;
  • быстрее воспринимается;
  • ближе к бизнес-логике.

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

Кастомные assertions особенно полезны:

  • в больших проектах;
  • при командной разработке;
  • при повторяющихся сценариях;
  • в e2e тестах с бизнес-логикой.

Например:

  • проверка авторизации;
  • проверка состояния корзины;
  • проверка ролей пользователя;
  • проверка отображения данных.

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

Кастомизация assertions — это способ сделать проверки более удобными, читаемыми и переиспользуемыми.

Ключевая идея в том, что тест должен описывать поведение на уровне смысла, а не деталей.

Вместо: «проверь текст, проверь кнопку, проверь аватар»

мы приходим к: «проверь, что пользователь залогинен».

Это:

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

Хорошие assertions — это не просто проверки, а язык, на котором тесты «разговаривают» с приложением.