Зарегистрируйтесь, чтобы продолжить обучение

Состояние Тестирование с Playwright

Приложение TodoMVC сохраняет свои данные только в браузере (в localstorage), поэтому его удобно тестировать. Повторный запуск тестов, гарантировано работает с вновь открытым браузером, в котором приложение находится всегда в одном и том же состоянии с пустым списком задач.

Более того, каждый тест в Playwright запускается в новом контексте браузера (BrowserContext). Это означает, что каждый тест получает изолированную среду, в которой обнуляются следующие элементы:

  • Cookies: Все куки, установленные в браузере, не сохраняются между тестами.
  • Local Storage: Все локальное хранилище очищается.
  • Session Storage: Вся сессионная информация очищается.
  • Cache: Кэш браузера также изолирован между контекстами.
  • Service Workers: Все зарегистрированные сервис-воркеры будут сброшены.

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

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

Подходы к восстановлению состояния зависят от того, как организован процесс тестирования. Есть два основных способа:

  • Сайт запущен где-то и существует независимо от тестов. Таким местом может быть стейджинг.
  • Сайт запускается во время тестов и управляется со стороны тестировщика.

Сайт не управляется тестами

В первом случае возможности по очистке состояния ограничены. Мы можем:

  1. Выполнение действий в браузере:
    • Это может включать удаление созданных элементов, сброс формы и другие действия, необходимые для восстановления исходного состояния. Однако этот подход не всегда надежен и может быть сложным в реализации для сложных сценариев.
    • Пример: После создания нового пользователя, можно выполнить действия по удалению этого пользователя в браузере. Это работает для простых случаев, но становится сложным для более комплексных взаимодействий.
  2. Создание новых пользователей для каждого теста:
    • Один из способов избежать очистки данных - создание новых пользователей для каждого теста. Это может быть полезно для тестирования пользовательских сценариев, не влияя на существующих пользователей.
    • Пример: Каждый тест создает нового пользователя с уникальным именем и использует его для тестов. Это гарантирует, что данные предыдущих тестов не повлияют на текущий тест.
  3. Использование API для сброса состояния:
    • Попросить разработчиков создать специальные API-эндпоинты для сброса состояния базы данных или отдельных данных. Эти эндпоинты могут вызываться в хуках перед или после тестов для подготовки необходимого состояния.
    • Пример: API-эндпоинт /api/reset может сбрасывать базу данных в начальное состояние. javascript test.beforeEach(async ({ request }) => { // Вызов API для сброса состояния базы данных перед каждым тестом await request.post('http://your-api-endpoint/reset') })

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

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

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

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

Сайт управляется тестами

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

import { defineConfig } from '@playwright/test'

export default defineConfig({
  // Запуск локального сервера
  // Скорее всего тут будет docker
  webServer: {
    command: 'npm run start',
    url: 'http://127.0.0.1:3000',
    reuseExistingServer: !process.env.CI,
    stdout: 'ignore',
    stderr: 'pipe',
  },
})

Во-вторых, мы можем взаимодействовать с сайтом не только по API, но и через консольные команды, которые мы либо напишем сами, либо нам предоставит команда разработки. Что с их помощью можно делать:

  • Подготавливать данные перед запуском тестов. Технически это может быть реализовано через восстановление бекапа.
  • Восстанавливать состояние перед каждым тестом с помощью фикстур.

Организовано это может быть так. Перед запуском всех тестов пересоздается база данных с нужной структрой. Перед запуском каждого теста восстанавливаются данные с помощью, например, фикстур.

Глобальные хуки

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

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

export default defineConfig({
  // другие опции
  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },

    {
      name: 'firefox',
      use: { ...devices['Desktop Firefox'] },
    },

    {
      name: 'webkit',
      use: { ...devices['Desktop Safari'] },
    },
  ],
})

Проекты можно добавлять и удалять, а еще они поддерживают указание зависимостей. То есть один проект может зависеть от другого. Технически это означает что зависимые проекты, сначала дожидаются выполнения тех проектов, от которых они зависят. Так мы можем добавить общую логику.

export default defineConfig({
  projects: [
    {
      name: 'setup db',
      testMatch: /global\.setup\.ts/,
    },
    {
      name: 'chromium with db',
      use: { ...devices['Desktop Chrome'] },
      dependencies: ['setup db'], // имя зависимости это имя проекта
    },
    // остальные проекты
  ],
})

Такая конфигурация приведет к выполнению файла global.setup.ts до запуска тестов. Сам файл global.setup.ts может выглядеть так:

// Переименовываем, чтобы не путаться, так как тут нет тестов
import { test as setup } from '@playwright/test'

setup('create new database', async ({ }) => {
  // Какие-то действия с базой данных
})

Дополнительные материалы

  1. playwright.dev

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff