Spec-Zone.ru › Playwright

Аутентификация

Введение​

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

Основные понятия​

Независимо от выбранной стратегии аутентификации, скорее всего, вы будете хранить состояние аутентификации браузера в файловой системе.

Рекомендуем создать каталог playwright/.auth и добавить его в .gitignore. Процедура аутентификации создаст состояние аутентификации браузера и сохранит его в файл в этом каталоге playwright/.auth. Затем тесты повторно используют это состояние и запускаются уже с выполненной аутентификацией.

опасно

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

  • Bash
  • PowerShell
  • Batch
mkdir -p playwright/.auth
echo $'\nplaywright/.auth' >> .gitignore
New-Item -ItemType Directory -Force -Path playwright\.auth
Add-Content -path .gitignore "`r`nplaywright/.auth"
md playwright\.auth
echo. >> .gitignore
echo "playwright/.auth" >> .gitignore

Базовый вариант: общая учётная запись для всех тестов​

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

Когда использовать

  • Если все тесты могут выполняться одновременно с одной учётной записью, не влияя друг на друга.

Когда не использовать

  • Тесты изменяют состояние на стороне сервера. Например, один тест проверяет отображение страницы настроек, а другой изменяет настройку, и тесты запускаются параллельно. В этом случае тестам нужны разные учётные записи.
  • Аутентификация зависит от браузера.

Подробности

Создайте tests/auth.setup.ts, который подготовит состояние аутентификации браузера для всех остальных тестов.

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

const authFile = path.join(__dirname, '../playwright/.auth/user.json');

setup('authenticate', async ({ page }) => {
  // Perform authentication steps. Replace these actions with your own.
  await page.goto('https://github.com/login');
  await page.getByLabel('Username or email address').fill('username');
  await page.getByLabel('Password').fill('password');
  await page.getByRole('button', { name: 'Sign in' }).click();
  // Wait until the page receives the cookies.
  //
  // Sometimes login flow sets cookies in the process of several redirects.
  // Wait for the final URL to ensure that the cookies are actually set.
  await page.waitForURL('https://github.com/');
  // Alternatively, you can wait until the page reaches a state where all cookies are set.
  await expect(page.getByRole('button', { name: 'View profile and more' })).toBeVisible();

  // End of authentication steps.

  await page.context().storageState({ path: authFile });
});

Создайте в конфигурации новый проект setup и объявите его зависимостью для всех проектов тестирования. Этот проект будет запускаться первым и выполнять аутентификацию перед всеми тестами. Все проекты тестирования должны использовать состояние аутентификации в качестве storageState.

import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  projects: [
    // Setup project
    { name: 'setup', testMatch: /.*\.setup\.ts/ },

    {
      name: 'chromium',
      use: {
        ...devices['Desktop Chrome'],
        // Use prepared auth state.
        storageState: 'playwright/.auth/user.json',
      },
      dependencies: ['setup'],
    },

    {
      name: 'firefox',
      use: {
        ...devices['Desktop Firefox'],
        // Use prepared auth state.
        storageState: 'playwright/.auth/user.json',
      },
      dependencies: ['setup'],
    },
  ],
});

Тесты запускаются уже с выполненной аутентификацией, поскольку в конфигурации указано storageState.

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

test('test', async ({ page }) => {
  // page is authenticated
});

Не забывайте удалять сохранённое состояние по истечении срока его действия. Если состояние не нужно сохранять между запусками тестов, записывайте состояние браузера в testProject.outputDir: его содержимое автоматически очищается перед каждым запуском тестов.

Аутентификация в режиме UI​

Для ускорения тестирования режим UI по умолчанию не запускает проект setup. Рекомендуем время от времени выполнять аутентификацию вручную, запуская auth.setup.ts, когда срок действия существующей аутентификации истечёт.

Сначала включите проект setup в фильтрах, затем нажмите кнопку с треугольником рядом с файлом auth.setup.ts, после чего снова отключите проект setup в фильтрах.

Умеренный вариант: одна учётная запись на параллельный рабочий процесс​

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

Когда использовать

  • Тесты изменяют общее состояние на стороне сервера. Например, один тест проверяет отображение страницы настроек, а другой изменяет настройку.

Когда не использовать

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

Подробности

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

Создайте файл playwright/fixtures.ts, который будет переопределять фикстуру storageState, чтобы выполнять аутентификацию один раз для каждого рабочего процесса. Используйте testInfo.parallelIndex, чтобы различать рабочие процессы.

import { test as baseTest, expect } from '@playwright/test';
import fs from 'fs';
import path from 'path';

export * from '@playwright/test';
export const test = baseTest.extend<{}, { workerStorageState: string }>({
  // Use the same storage state for all tests in this worker.
  storageState: ({ workerStorageState }, use) => use(workerStorageState),

  // Authenticate once per worker with a worker-scoped fixture.
  workerStorageState: [async ({ browser }, use) => {
    // Use parallelIndex as a unique identifier for each worker.
    const id = test.info().parallelIndex;
    const fileName = path.resolve(test.info().project.outputDir, `.auth/${id}.json`);

    if (fs.existsSync(fileName)) {
      // Reuse existing authentication state if any.
      await use(fileName);
      return;
    }

    // Important: make sure we authenticate in a clean environment by unsetting storage state.
    const page = await browser.newPage({ storageState: undefined });

    // Acquire a unique account, for example create a new one.
    // Alternatively, you can have a list of precreated accounts for testing.
    // Make sure that accounts are unique, so that multiple team members
    // can run tests at the same time without interference.
    const account = await acquireAccount(id);

    // Perform authentication steps. Replace these actions with your own.
    await page.goto('https://github.com/login');
    await page.getByLabel('Username or email address').fill(account.username);
    await page.getByLabel('Password').fill(account.password);
    await page.getByRole('button', { name: 'Sign in' }).click();
    // Wait until the page receives the cookies.
    //
    // Sometimes login flow sets cookies in the process of several redirects.
    // Wait for the final URL to ensure that the cookies are actually set.
    await page.waitForURL('https://github.com/');
    // Alternatively, you can wait until the page reaches a state where all cookies are set.
    await expect(page.getByRole('button', { name: 'View profile and more' })).toBeVisible();

    // End of authentication steps.

    await page.context().storageState({ path: fileName });
    await page.close();
    await use(fileName);
  }, { scope: 'worker' }],
});

Теперь каждый файл тестов должен импортировать test из нашего файла фикстур вместо @playwright/test. Вносить изменения в конфигурацию не требуется.

// Important: import our fixtures.
import { test, expect } from '../playwright/fixtures';

test('test', async ({ page }) => {
  // page is authenticated
});

Расширенные сценарии​

Аутентификация с помощью API-запроса​

Когда использовать

  • Ваше веб-приложение поддерживает аутентификацию через API, которая проще или быстрее взаимодействия с интерфейсом приложения.

Подробности

Мы отправим API-запрос с помощью APIRequestContext, а затем сохраним состояние аутентификации обычным способом.

В проекте настройки:

import { test as setup } from '@playwright/test';

const authFile = 'playwright/.auth/user.json';

setup('authenticate', async ({ request }) => {
  // Send authentication request. Replace with your own.
  await request.post('https://github.com/login', {
    form: {
      'user': 'user',
      'password': 'password'
    }
  });
  await request.storageState({ path: authFile });
});

Или в фикстуре рабочего процесса:

import { test as baseTest, request } from '@playwright/test';
import fs from 'fs';
import path from 'path';

export * from '@playwright/test';
export const test = baseTest.extend<{}, { workerStorageState: string }>({
  // Use the same storage state for all tests in this worker.
  storageState: ({ workerStorageState }, use) => use(workerStorageState),

  // Authenticate once per worker with a worker-scoped fixture.
  workerStorageState: [async ({}, use) => {
    // Use parallelIndex as a unique identifier for each worker.
    const id = test.info().parallelIndex;
    const fileName = path.resolve(test.info().project.outputDir, `.auth/${id}.json`);

    if (fs.existsSync(fileName)) {
      // Reuse existing authentication state if any.
      await use(fileName);
      return;
    }

    // Important: make sure we authenticate in a clean environment by unsetting storage state.
    const context = await request.newContext({ storageState: undefined });

    // Acquire a unique account, for example create a new one.
    // Alternatively, you can have a list of precreated accounts for testing.
    // Make sure that accounts are unique, so that multiple team members
    // can run tests at the same time without interference.
    const account = await acquireAccount(id);

    // Send authentication request. Replace with your own.
    await context.post('https://github.com/login', {
      form: {
        'user': 'user',
        'password': 'password'
      }
    });

    await context.storageState({ path: fileName });
    await context.dispose();
    await use(fileName);
  }, { scope: 'worker' }],
});

Несколько ролей с выполненной аутентификацией​

Когда использовать

  • В сквозных тестах используется несколько ролей, но учётные записи можно повторно использовать во всех тестах.

Подробности

В проекте настройки мы выполним аутентификацию несколько раз.

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

const adminFile = 'playwright/.auth/admin.json';

setup('authenticate as admin', async ({ page }) => {
  // Perform authentication steps. Replace these actions with your own.
  await page.goto('https://github.com/login');
  await page.getByLabel('Username or email address').fill('admin');
  await page.getByLabel('Password').fill('password');
  await page.getByRole('button', { name: 'Sign in' }).click();
  // Wait until the page receives the cookies.
  //
  // Sometimes login flow sets cookies in the process of several redirects.
  // Wait for the final URL to ensure that the cookies are actually set.
  await page.waitForURL('https://github.com/');
  // Alternatively, you can wait until the page reaches a state where all cookies are set.
  await expect(page.getByRole('button', { name: 'View profile and more' })).toBeVisible();

  // End of authentication steps.

  await page.context().storageState({ path: adminFile });
});

const userFile = 'playwright/.auth/user.json';

setup('authenticate as user', async ({ page }) => {
  // Perform authentication steps. Replace these actions with your own.
  await page.goto('https://github.com/login');
  await page.getByLabel('Username or email address').fill('user');
  await page.getByLabel('Password').fill('password');
  await page.getByRole('button', { name: 'Sign in' }).click();
  // Wait until the page receives the cookies.
  //
  // Sometimes login flow sets cookies in the process of several redirects.
  // Wait for the final URL to ensure that the cookies are actually set.
  await page.waitForURL('https://github.com/');
  // Alternatively, you can wait until the page reaches a state where all cookies are set.
  await expect(page.getByRole('button', { name: 'View profile and more' })).toBeVisible();

  // End of authentication steps.

  await page.context().storageState({ path: userFile });
});

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

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

test.use({ storageState: 'playwright/.auth/admin.json' });

test('admin test', async ({ page }) => {
  // page is authenticated as admin
});

test.describe(() => {
  test.use({ storageState: 'playwright/.auth/user.json' });

  test('user test', async ({ page }) => {
    // page is authenticated as a user
  });
});

См. также раздел об аутентификации в режиме UI.

Тестирование нескольких ролей одновременно​

Когда использовать

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

Подробности

В одном тесте используйте несколько BrowserContext и Page с разными состояниями хранилища.

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

test('admin and user', async ({ browser }) => {
  // adminContext and all pages inside, including adminPage, are signed in as "admin".
  const adminContext = await browser.newContext({ storageState: 'playwright/.auth/admin.json' });
  const adminPage = await adminContext.newPage();

  // userContext and all pages inside, including userPage, are signed in as "user".
  const userContext = await browser.newContext({ storageState: 'playwright/.auth/user.json' });
  const userPage = await userContext.newPage();

  // ... interact with both adminPage and userPage ...

  await adminContext.close();
  await userContext.close();
});

Тестирование нескольких ролей с помощью фикстур POM​

Когда использовать

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

Подробности

Можно добавить фикстуры, которые предоставляют страницу с выполненной аутентификацией для каждой роли.

Ниже приведён пример, в котором создаются фикстуры для двух моделей объектов страницы — POM администратора и POM пользователя. Предполагается, что в глобальной настройке созданы файлы adminStorageState.json и userStorageState.json.

import { test as base, type Page, type Locator } from '@playwright/test';

// Page Object Model for the "admin" page.
// Here you can add locators and helper methods specific to the admin page.
class AdminPage {
  // Page signed in as "admin".
  page: Page;

  // Example locator pointing to "Welcome, Admin" greeting.
  greeting: Locator;

  constructor(page: Page) {
    this.page = page;
    this.greeting = page.locator('#greeting');
  }
}

// Page Object Model for the "user" page.
// Here you can add locators and helper methods specific to the user page.
class UserPage {
  // Page signed in as "user".
  page: Page;

  // Example locator pointing to "Welcome, User" greeting.
  greeting: Locator;

  constructor(page: Page) {
    this.page = page;
    this.greeting = page.locator('#greeting');
  }
}

// Declare the types of your fixtures.
type MyFixtures = {
  adminPage: AdminPage;
  userPage: UserPage;
};

export * from '@playwright/test';
export const test = base.extend<MyFixtures>({
  adminPage: async ({ browser }, use) => {
    const context = await browser.newContext({ storageState: 'playwright/.auth/admin.json' });
    const adminPage = new AdminPage(await context.newPage());
    await use(adminPage);
    await context.close();
  },
  userPage: async ({ browser }, use) => {
    const context = await browser.newContext({ storageState: 'playwright/.auth/user.json' });
    const userPage = new UserPage(await context.newPage());
    await use(userPage);
    await context.close();
  },
});
// Import test with our new fixtures.
import { test, expect } from '../playwright/fixtures';

// Use adminPage and userPage fixtures in the test.
test('admin and user', async ({ adminPage, userPage }) => {
  // ... interact with both adminPage and userPage ...
  await expect(adminPage.greeting).toHaveText('Welcome, Admin');
  await expect(userPage.greeting).toHaveText('Welcome, User');
});

Хранилище сеанса​

Повторное использование состояния аутентификации охватывает аутентификацию на основе файлов cookie, локального хранилища, IndexedDB и ключей доступа (WebAuthn). В редких случаях для хранения информации о состоянии входа используется хранилище сеанса. Хранилище сеанса привязано к определённому домену и не сохраняется при загрузке страниц. В Playwright нет API для сохранения хранилища сеанса, однако приведённый ниже фрагмент кода можно использовать для сохранения и загрузки хранилища сеанса.

// Get session storage and store as env variable
const sessionStorage = await page.evaluate(() => JSON.stringify(sessionStorage));
fs.writeFileSync('playwright/.auth/session.json', sessionStorage, 'utf-8');

// Set session storage in a new context
const sessionStorage = JSON.parse(fs.readFileSync('playwright/.auth/session.json', 'utf-8'));
await context.addInitScript(storage => {
  if (window.location.hostname === 'example.com') {
    for (const [key, value] of Object.entries(storage))
      window.sessionStorage.setItem(key, value);
  }
}, sessionStorage);

Отключение аутентификации в некоторых тестах​

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

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

// Reset storage state for this file to avoid being authenticated
test.use({ storageState: { cookies: [], origins: [] } });

test('not signed in test', async ({ page }) => {
  // ...
});

© 2026 Microsoft
Licensed under the Apache License, Version 2.0.
https://playwright.dev/docs/auth

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API