Фикстуры
Введение
Playwright Test основан на концепции тестовых фикстур. Фикстуры используются для настройки окружения каждого теста, предоставляя ему всё необходимое и ничего лишнего. Фикстуры изолированы друг от друга. С помощью фикстур можно группировать тесты по смыслу, а не по общим настройкам.
Встроенные фикстуры
Вы уже использовали тестовые фикстуры в своём первом тесте.
import { test, expect } from '@playwright/test';
test('basic test', async ({ page }) => {
await page.goto('https://playwright.dev/');
await expect(page).toHaveTitle(/Playwright/);
}); Аргумент { page } указывает Playwright Test настроить фикстуру page и передать её функции теста.
Ниже приведён список предопределённых фикстур, которые, скорее всего, будут использоваться чаще всего:
| Фикстура | Тип | Описание |
|---|---|---|
| page | Page | Изолированная страница для этого запуска теста. |
| context | BrowserContext | Изолированный контекст для этого запуска теста. Фикстура page также принадлежит этому контексту. Узнайте, как настроить контекст. |
| browser | Browser | Браузеры используются несколькими тестами совместно для оптимизации ресурсов. Узнайте, как настроить браузеры. |
| browserName | string | Название браузера, в котором сейчас выполняется тест. Это может быть chromium, firefox или webkit. |
| request | APIRequestContext | Изолированный экземпляр APIRequestContext для этого запуска теста. |
Без фикстур
Ниже показано, чем обычно отличается настройка тестового окружения при традиционном подходе и при использовании фикстур.
TodoPage — это класс, который помогает взаимодействовать со страницей веб-приложения «список задач» согласно шаблону Page Object Model. Внутри он использует page из Playwright.
Нажмите, чтобы развернуть код для TodoPageimport type { Page, Locator } from '@playwright/test'; export class TodoPage { private readonly inputBox: Locator; private readonly todoItems: Locator; constructor(public readonly page: Page) { this.inputBox = this.page.locator('input.new-todo'); this.todoItems = this.page.getByTestId('todo-item'); } async goto() { await this.page.goto('https://demo.playwright.dev/todomvc/'); } async addToDo(text: string) { await this.inputBox.fill(text); await this.inputBox.press('Enter'); } async remove(text: string) { const todo = this.todoItems.filter({ hasText: text }); await todo.hover(); await todo.getByLabel('Delete').click(); } async removeAll() { while ((await this.todoItems.count()) > 0) { await this.todoItems.first().hover(); await this.todoItems.getByLabel('Delete').first().click(); } } }
const { test } = require('@playwright/test');
const { TodoPage } = require('./todo-page');
test.describe('todo tests', () => {
let todoPage;
test.beforeEach(async ({ page }) => {
todoPage = new TodoPage(page);
await todoPage.goto();
await todoPage.addToDo('item1');
await todoPage.addToDo('item2');
});
test.afterEach(async () => {
await todoPage.removeAll();
});
test('should add an item', async () => {
await todoPage.addToDo('my item');
// ...
});
test('should remove an item', async () => {
await todoPage.remove('item1');
// ...
});
}); С фикстурами
Фикстуры обладают рядом преимуществ по сравнению с хуками before/after:
- Фикстуры инкапсулируют настройку и очистку в одном месте, упрощая написание кода. Поэтому, если у вас есть хук after, который очищает то, что было создано в хуке before, рассмотрите возможность преобразовать их в фикстуру.
- Фикстуры можно повторно использовать в разных файлах тестов: достаточно определить их один раз и применять во всех тестах. Так работает встроенная фикстура
pageв Playwright. Поэтому, если вспомогательная функция используется в нескольких тестах, рассмотрите возможность преобразовать её в фикстуру. - Фикстуры создаются по запросу: можно определить сколько угодно фикстур, а Playwright Test настроит только те, которые нужны тесту, и ничего лишнего.
- Фикстуры компонуются: они могут зависеть друг от друга, обеспечивая сложное поведение.
- Фикстуры гибкие. Тесты могут использовать любые сочетания фикстур, чтобы точно настроить окружение под свои требования, не влияя на другие тесты.
- Фикстуры упрощают группировку. Больше не нужно оборачивать тесты в
describe, которые настраивают окружение: теперь тесты можно свободно группировать по смыслу.
Нажмите, чтобы развернуть код для TodoPageimport type { Page, Locator } from '@playwright/test'; export class TodoPage { private readonly inputBox: Locator; private readonly todoItems: Locator; constructor(public readonly page: Page) { this.inputBox = this.page.locator('input.new-todo'); this.todoItems = this.page.getByTestId('todo-item'); } async goto() { await this.page.goto('https://demo.playwright.dev/todomvc/'); } async addToDo(text: string) { await this.inputBox.fill(text); await this.inputBox.press('Enter'); } async remove(text: string) { const todo = this.todoItems.filter({ hasText: text }); await todo.hover(); await todo.getByLabel('Delete').click(); } async removeAll() { while ((await this.todoItems.count()) > 0) { await this.todoItems.first().hover(); await this.todoItems.getByLabel('Delete').first().click(); } } }
import { test as base } from '@playwright/test';
import { TodoPage } from './todo-page';
// Extend basic test by providing a "todoPage" fixture.
const test = base.extend<{ todoPage: TodoPage }>({
todoPage: async ({ page }, use) => {
const todoPage = new TodoPage(page);
await todoPage.goto();
await todoPage.addToDo('item1');
await todoPage.addToDo('item2');
await use(todoPage);
await todoPage.removeAll();
},
});
test('should add an item', async ({ todoPage }) => {
await todoPage.addToDo('my item');
// ...
});
test('should remove an item', async ({ todoPage }) => {
await todoPage.remove('item1');
// ...
}); Создание фикстуры
Чтобы создать собственную фикстуру, используйте test.extend() для создания нового объекта test, который будет включать её.
Ниже мы создаём две фикстуры, todoPage и settingsPage, соответствующие шаблону Page Object Model.
Нажмите, чтобы развернуть код для TodoPageиSettingsPageimport type { Page, Locator } from '@playwright/test'; export class TodoPage { private readonly inputBox: Locator; private readonly todoItems: Locator; constructor(public readonly page: Page) { this.inputBox = this.page.locator('input.new-todo'); this.todoItems = this.page.getByTestId('todo-item'); } async goto() { await this.page.goto('https://demo.playwright.dev/todomvc/'); } async addToDo(text: string) { await this.inputBox.fill(text); await this.inputBox.press('Enter'); } async remove(text: string) { const todo = this.todoItems.filter({ hasText: text }); await todo.hover(); await todo.getByLabel('Delete').click(); } async removeAll() { while ((await this.todoItems.count()) > 0) { await this.todoItems.first().hover(); await this.todoItems.getByLabel('Delete').first().click(); } } }SettingsPage устроена аналогично:
import type { Page } from '@playwright/test'; export class SettingsPage { constructor(public readonly page: Page) { } async switchToDarkMode() { // ... } }
import { test as base } from '@playwright/test';
import { TodoPage } from './todo-page';
import { SettingsPage } from './settings-page';
// Declare the types of your fixtures.
type MyFixtures = {
todoPage: TodoPage;
settingsPage: SettingsPage;
};
// Extend base test by providing "todoPage" and "settingsPage".
// This new "test" can be used in multiple test files, and each of them will get the fixtures.
export const test = base.extend<MyFixtures>({
todoPage: async ({ page }, use) => {
// Set up the fixture.
const todoPage = new TodoPage(page);
await todoPage.goto();
await todoPage.addToDo('item1');
await todoPage.addToDo('item2');
// Use the fixture value in the test.
await use(todoPage);
// Clean up the fixture.
await todoPage.removeAll();
},
settingsPage: async ({ page }, use) => {
await use(new SettingsPage(page));
},
});
export { expect } from '@playwright/test'; примечаниеИмена пользовательских фикстур должны начинаться с буквы или символа подчёркивания и содержать только буквы, цифры и символы подчёркивания.
Использование фикстуры
Просто укажите фикстуру в аргументах функции теста, и тестовый исполнитель позаботится обо всём остальном. Фикстуры также доступны в хуках и других фикстурах. При использовании TypeScript типы фикстур будут проверяться.
Ниже используются фикстуры todoPage и settingsPage, определённые выше.
import { test, expect } from './my-test';
test.beforeEach(async ({ settingsPage }) => {
await settingsPage.switchToDarkMode();
});
test('basic test', async ({ todoPage, page }) => {
await todoPage.addToDo('something nice');
await expect(page.getByTestId('todo-title')).toContainText(['something nice']);
}); Переопределение фикстур
Помимо создания собственных фикстур, можно переопределять существующие в соответствии со своими потребностями. Рассмотрим следующий пример, в котором переопределяется фикстура page и выполняется автоматический переход на baseURL:
import { test as base } from '@playwright/test';
export const test = base.extend({
page: async ({ baseURL, page }, use) => {
await page.goto(baseURL);
await use(page);
},
}); Обратите внимание: в этом примере фикстура page может зависеть от других встроенных фикстур, например testOptions.baseURL. Теперь можно настроить baseURL в файле конфигурации или локально в файле теста с помощью test.use().
test.use({ baseURL: 'https://playwright.dev' }); Фикстуры также можно переопределить, полностью заменив базовую фикстуру на другую. Например, можно переопределить фикстуру testOptions.storageState, чтобы предоставить собственные данные.
import { test as base } from '@playwright/test';
export const test = base.extend({
storageState: async ({}, use) => {
const cookie = await getAuthCookie();
await use({ cookies: [cookie] });
},
}); Фикстуры уровня рабочего процесса
Playwright Test использует рабочие процессы для запуска файлов тестов. Подобно тому, как тестовые фикстуры настраиваются для отдельных запусков тестов, фикстуры рабочего процесса настраиваются для каждого рабочего процесса. Здесь можно настроить службы, запустить серверы и так далее. Playwright Test повторно использует рабочий процесс для максимально возможного числа файлов тестов, если их фикстуры рабочего процесса совпадают, а значит, окружения идентичны.
Ниже мы создадим фикстуру account, общую для всех тестов в одном рабочем процессе, и переопределим фикстуру page, чтобы выполнять вход в эту учётную запись для каждого теста. Для создания уникальных учётных записей мы воспользуемся workerInfo.workerIndex, доступным любому тесту или фикстуре. Обратите внимание на синтаксис кортежа для фикстуры рабочего процесса: необходимо передать {scope: 'worker'}, чтобы тестовый исполнитель настраивал эту фикстуру один раз для каждого рабочего процесса.
Помимо выполнения только один раз для каждого рабочего процесса, фикстуры уровня рабочего процесса также получают отдельный тайм-аут, равный тайм-ауту теста по умолчанию. Его можно изменить, передав параметр timeout. Подробнее см. в разделе Тайм-аут фикстуры.
import { test as base } from '@playwright/test';
type Account = {
username: string;
password: string;
};
// Note that we pass worker fixture types as a second template parameter.
export const test = base.extend<{}, { account: Account }>({
account: [async ({ browser }, use, workerInfo) => {
// Unique username.
const username = 'user' + workerInfo.workerIndex;
const password = 'verysecure';
// Create the account with Playwright.
const page = await browser.newPage();
await page.goto('/signup');
await page.getByLabel('User Name').fill(username);
await page.getByLabel('Password').fill(password);
await page.getByText('Sign up').click();
// Make sure everything is ok.
await expect(page.getByTestId('result')).toHaveText('Success');
// Do not forget to cleanup.
await page.close();
// Use the account value.
await use({ username, password });
}, { scope: 'worker' }],
page: async ({ page, account }, use) => {
// Sign in with our account.
const { username, password } = account;
await page.goto('/signin');
await page.getByLabel('User Name').fill(username);
await page.getByLabel('Password').fill(password);
await page.getByText('Sign in').click();
await expect(page.getByTestId('userinfo')).toHaveText(username);
// Use signed-in page in the test.
await use(page);
},
});
export { expect } from '@playwright/test'; Автоматические фикстуры
Автоматические фикстуры настраиваются для каждого теста или рабочего процесса, даже если тест явно их не указывает. Чтобы создать автоматическую фикстуру, используйте синтаксис кортежа и передайте { auto: true }.
Ниже приведён пример фикстуры, которая автоматически сохраняет журналы отладки при сбое теста, чтобы позднее их можно было просмотреть в средстве формирования отчётов. Обратите внимание, что она использует объект TestInfo, доступный в каждом тесте и фикстуре для получения метаданных о выполняемом тесте.
import debug from 'debug';
import fs from 'fs';
import { test as base } from '@playwright/test';
export const test = base.extend<{ saveLogs: void }>({
saveLogs: [async ({}, use, testInfo) => {
// Collecting logs during the test.
const logs = [];
debug.log = (...args) => logs.push(args.map(String).join(''));
debug.enable('myserver');
await use();
// After the test we can check whether the test passed or failed.
if (testInfo.status !== testInfo.expectedStatus) {
// outputPath() API guarantees a unique file name.
const logFile = testInfo.outputPath('logs.txt');
await fs.promises.writeFile(logFile, logs.join('\n'), 'utf8');
testInfo.attachments.push({ name: 'logs', contentType: 'text/plain', path: logFile });
}
}, { auto: true }],
});
export { expect } from '@playwright/test'; Тайм-аут фикстуры
Фикстура считается частью теста, поэтому время её настройки и очистки учитывается в тайм-ауте теста. Таким образом, медленная фикстура может привести к превышению тайм-аута теста. Для такой фикстуры можно задать отдельный, более длительный тайм-аут и оставить общий тайм-аут теста небольшим.
import { test as base, expect } from '@playwright/test';
const test = base.extend<{ slowFixture: string }>({
slowFixture: [async ({}, use) => {
// ... perform a slow operation ...
await use('hello');
}, { timeout: 60000 }]
});
test('example test', async ({ slowFixture }) => {
// ...
}); В отличие от обычных фикстур уровня теста, у каждой фикстуры уровня рабочего процесса есть собственный тайм-аут, равный тайм-ауту теста. Тайм-аут фикстуры уровня рабочего процесса можно изменить тем же способом.
Фикстуры-параметры
Playwright Test поддерживает запуск нескольких проектов тестирования, которые можно настраивать независимо друг от друга. С помощью фикстур-параметров можно сделать параметры конфигурации декларативными и обеспечить проверку типов. Подробнее см. в разделе Параметризация тестов.
Ниже мы создадим параметр defaultItem в дополнение к фикстуре todoPage из предыдущих примеров. Этот параметр будет задан в файле конфигурации. Обратите внимание на синтаксис кортежа и аргумент { option: true }.
Нажмите, чтобы развернуть код для TodoPageimport type { Page, Locator } from '@playwright/test'; export class TodoPage { private readonly inputBox: Locator; private readonly todoItems: Locator; constructor(public readonly page: Page) { this.inputBox = this.page.locator('input.new-todo'); this.todoItems = this.page.getByTestId('todo-item'); } async goto() { await this.page.goto('https://demo.playwright.dev/todomvc/'); } async addToDo(text: string) { await this.inputBox.fill(text); await this.inputBox.press('Enter'); } async remove(text: string) { const todo = this.todoItems.filter({ hasText: text }); await todo.hover(); await todo.getByLabel('Delete').click(); } async removeAll() { while ((await this.todoItems.count()) > 0) { await this.todoItems.first().hover(); await this.todoItems.getByLabel('Delete').first().click(); } } }
import { test as base } from '@playwright/test';
import { TodoPage } from './todo-page';
// Declare your options to type-check your configuration.
export type MyOptions = {
defaultItem: string;
};
type MyFixtures = {
todoPage: TodoPage;
};
// Specify both option and fixture types.
export const test = base.extend<MyOptions & MyFixtures>({
// Define an option and provide a default value.
// We can later override it in the config.
defaultItem: ['Something nice', { option: true }],
// Our "todoPage" fixture depends on the option.
todoPage: async ({ page, defaultItem }, use) => {
const todoPage = new TodoPage(page);
await todoPage.goto();
await todoPage.addToDo(defaultItem);
await use(todoPage);
await todoPage.removeAll();
},
});
export { expect } from '@playwright/test'; Теперь фикстуру todoPage можно использовать как обычно, а параметр defaultItem задать в файле конфигурации.
import { defineConfig } from '@playwright/test';
import type { MyOptions } from './my-test';
export default defineConfig<MyOptions>({
projects: [
{
name: 'shopping',
use: { defaultItem: 'Buy milk' },
},
{
name: 'wellbeing',
use: { defaultItem: 'Exercise!' },
},
]
}); Массив в качестве значения параметра
Если значение параметра представляет собой массив, например [{ name: 'Alice' }, { name: 'Bob' }], при передаче значения его нужно заключить в дополнительный массив. Лучше всего это показать на примере.
type Person = { name: string };
const test = base.extend<{ persons: Person[] }>({
// Declare the option, default value is an empty array.
persons: [[], { option: true }],
});
// Option value is an array of persons.
const actualPersons = [{ name: 'Alice' }, { name: 'Bob' }];
test.use({
// CORRECT: Wrap the value into an array and pass the scope.
persons: [actualPersons, { scope: 'test' }],
});
test.use({
// WRONG: passing an array value directly will not work.
persons: actualPersons,
}); Сброс параметра
Параметр можно сбросить до значения, заданного в файле конфигурации, присвоив ему undefined. Рассмотрим следующую конфигурацию, которая задаёт baseURL:
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
baseURL: 'https://playwright.dev',
},
}); Теперь можно настроить baseURL для файла, а также отключить его для отдельного теста.
import { test } from '@playwright/test';
// Configure baseURL for this file.
test.use({ baseURL: 'https://playwright.dev/docs/intro' });
test('check intro contents', async ({ page }) => {
// This test will use "https://playwright.dev/docs/intro" base url as defined above.
});
test.describe(() => {
// Reset the value to a config-defined one.
test.use({ baseURL: undefined });
test('can navigate to intro from the home page', async ({ page }) => {
// This test will use "https://playwright.dev" base url as defined in the config.
});
}); Чтобы полностью сбросить значение до undefined, используйте полную форму записи фикстуры.
import { test } from '@playwright/test';
// Completely unset baseURL for this file.
test.use({
baseURL: [async ({}, use) => use(undefined), { scope: 'test' }],
});
test('no base url', async ({ page }) => {
// This test will not have a base url.
}); Порядок выполнения
У каждой фикстуры есть этапы настройки и очистки, выполняемые до и после вызова await use() внутри фикстуры. Настройка выполняется до запуска теста или хука, которому нужна фикстура, а очистка — когда тест или хук перестаёт её использовать.
Порядок выполнения фикстур определяется следующими правилами:
- Если фикстура A зависит от фикстуры B, то B всегда настраивается до A и очищается после A.
- Неавтоматические фикстуры выполняются по требованию, только когда они нужны тесту или хуку.
- Фикстуры уровня теста очищаются после каждого теста, а фикстуры уровня рабочего процесса — только после завершения рабочего процесса, выполняющего тесты.
Рассмотрим следующий пример:
import { test as base } from '@playwright/test';
const test = base.extend<{
testFixture: string,
autoTestFixture: string,
unusedFixture: string,
}, {
workerFixture: string,
autoWorkerFixture: string,
}>({
workerFixture: [async ({ browser }) => {
// workerFixture setup...
await use('workerFixture');
// workerFixture teardown...
}, { scope: 'worker' }],
autoWorkerFixture: [async ({ browser }) => {
// autoWorkerFixture setup...
await use('autoWorkerFixture');
// autoWorkerFixture teardown...
}, { scope: 'worker', auto: true }],
testFixture: [async ({ page, workerFixture }) => {
// testFixture setup...
await use('testFixture');
// testFixture teardown...
}, { scope: 'test' }],
autoTestFixture: [async () => {
// autoTestFixture setup...
await use('autoTestFixture');
// autoTestFixture teardown...
}, { scope: 'test', auto: true }],
unusedFixture: [async ({ page }) => {
// unusedFixture setup...
await use('unusedFixture');
// unusedFixture teardown...
}, { scope: 'test' }],
});
test.beforeAll(async () => { /* ... */ });
test.beforeEach(async ({ page }) => { /* ... */ });
test('first test', async ({ page }) => { /* ... */ });
test('second test', async ({ testFixture }) => { /* ... */ });
test.afterEach(async () => { /* ... */ });
test.afterAll(async () => { /* ... */ }); Обычно, если все тесты проходят успешно и ошибок не возникает, порядок выполнения будет следующим.
- настройка рабочего процесса и раздел
beforeAll:-
настройка
browser, поскольку она требуется дляautoWorkerFixture. -
настройка
autoWorkerFixture, поскольку автоматические фикстуры рабочего процесса всегда настраиваются в первую очередь. -
выполняется
beforeAll.
-
настройка
-
раздел
first test:-
настройка
autoTestFixture, поскольку автоматические фикстуры теста всегда настраиваются перед тестом и хукамиbeforeEach. -
настройка
page, поскольку она требуется в хукеbeforeEach. -
выполняется
beforeEach. -
выполняется
first test. -
выполняется
afterEach. -
очистка
page, поскольку это фикстура уровня теста, которую нужно очистить после завершения теста. -
очистка
autoTestFixture, поскольку это фикстура уровня теста, которую нужно очистить после завершения теста.
-
настройка
-
раздел
second test:-
настройка
autoTestFixture, поскольку автоматические фикстуры теста всегда настраиваются перед тестом и хукамиbeforeEach. -
настройка
page, поскольку она требуется в хукеbeforeEach. -
выполняется
beforeEach. -
настройка
workerFixture, поскольку она требуется дляtestFixture, необходимой дляsecond test. -
настройка
testFixture, поскольку она требуется дляsecond test. -
выполняется
second test. -
выполняется
afterEach. -
очистка
testFixture, поскольку это фикстура уровня теста, которую нужно очистить после завершения теста. -
очистка
page, поскольку это фикстура уровня теста, которую нужно очистить после завершения теста. -
очистка
autoTestFixture, поскольку это фикстура уровня теста, которую нужно очистить после завершения теста.
-
настройка
-
afterAllи раздел очистки рабочего процесса:-
выполняется
afterAll. -
очистка
workerFixture, поскольку это фикстура уровня рабочего процесса, которую нужно очистить один раз в конце. -
очистка
autoWorkerFixture, поскольку это фикстура уровня рабочего процесса, которую нужно очистить один раз в конце. -
очистка
browser, поскольку это фикстура уровня рабочего процесса, которую нужно очистить один раз в конце.
-
выполняется
Несколько замечаний:
-
pageиautoTestFixtureнастраиваются и очищаются для каждого теста, поскольку это фикстуры уровня теста. -
unusedFixtureникогда не настраивается, поскольку ни один тест или хук её не использует. -
testFixtureзависит отworkerFixtureи запускает её настройку. -
workerFixtureнастраивается по требованию перед вторым тестом, но очищается один раз при завершении рабочего процесса, поскольку это фикстура уровня рабочего процесса. -
autoWorkerFixtureнастраивается для хукаbeforeAll, аautoTestFixture— нет.
Объединение пользовательских фикстур из нескольких модулей
Можно объединить тестовые фикстуры из нескольких файлов или модулей:
import { mergeTests } from '@playwright/test';
import { test as dbTest } from 'database-test-utils';
import { test as a11yTest } from 'a11y-test-utils';
export const test = mergeTests(dbTest, a11yTest); import { test } from './fixtures';
test('passes', async ({ database, page, a11y }) => {
// use database and a11y fixtures.
}); Изолированные фикстуры
Обычно пользовательские фикстуры отображаются в виде отдельных шагов в режиме UI, Trace Viewer и различных отчётах о тестировании. Они также появляются в сообщениях об ошибках тестового исполнителя. Для часто используемых фикстур это может создавать много лишней информации. Чтобы фикстуры не отображались в режиме UI, их можно «изолировать».
import { test as base } from '@playwright/test';
export const test = base.extend({
helperFixture: [async ({}, use, testInfo) => {
// ...
}, { box: true }],
}); Это полезно для вспомогательных фикстур, которые не представляют интереса. Например, автоматическую фикстуру, настраивающую общие данные, можно безопасно скрыть в отчёте о тестировании.
Также можно задать для фикстуры значение box: 'self', чтобы скрыть только её, но при этом включить все шаги внутри фикстуры в отчёт о тестировании.
Пользовательское название фикстуры
Вместо обычного имени фикстуре можно задать пользовательское название, которое будет отображаться в отчётах о тестировании и сообщениях об ошибках.
import { test as base } from '@playwright/test';
export const test = base.extend({
innerFixture: [async ({}, use, testInfo) => {
// ...
}, { title: 'my fixture' }],
}); Добавление глобальных хуков beforeEach/afterEach
Хуки test.beforeEach() и test.afterEach() выполняются до и после каждого теста, объявленного в том же файле и том же блоке test.describe() (если он есть). Если нужно объявить хуки, которые выполняются до и после каждого теста глобально, их можно объявить как автоматические фикстуры следующим образом:
import { test as base } from '@playwright/test';
export const test = base.extend<{ forEachTest: void }>({
forEachTest: [async ({ page }, use) => {
// This code runs before every test.
await page.goto('http://localhost:8000');
await use();
// This code runs after every test.
console.log('Last URL:', page.url());
}, { auto: true }], // automatically starts for every test.
}); Затем импортируйте фикстуры во все тесты:
import { test } from './fixtures';
import { expect } from '@playwright/test';
test('basic', async ({ page }) => {
expect(page).toHaveURL('http://localhost:8000');
await page.goto('https://playwright.dev');
}); Добавление глобальных хуков beforeAll/afterAll
Хуки test.beforeAll() и test.afterAll() выполняются до и после всех тестов, объявленных в том же файле и том же блоке test.describe() (если он есть), один раз для каждого рабочего процесса. Если нужно объявить хуки, которые выполняются до и после всех тестов во всех файлах, их можно объявить как автоматические фикстуры с scope: 'worker' следующим образом:
import { test as base } from '@playwright/test';
export const test = base.extend<{}, { forEachWorker: void }>({
forEachWorker: [async ({}, use) => {
// This code runs before all the tests in the worker process.
console.log(`Starting test worker ${test.info().workerIndex}`);
await use();
// This code runs after all the tests in the worker process.
console.log(`Stopping test worker ${test.info().workerIndex}`);
}, { scope: 'worker', auto: true }], // automatically starts for every worker.
}); Затем импортируйте фикстуры во все тесты:
import { test } from './fixtures';
import { expect } from '@playwright/test';
test('basic', async ({ }) => {
// ...
}); Обратите внимание: фикстуры всё равно будут выполняться один раз для каждого рабочего процесса, но объявлять их заново в каждом файле не потребуется.
© 2026 Microsoft
Licensed under the Apache License, Version 2.0.
https://playwright.dev/docs/test-fixtures