Аутентификация
Введение
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