Параллельное выполнение
Введение
Playwright Test запускает тесты параллельно. Для этого он запускает несколько рабочих процессов, которые выполняются одновременно. По умолчанию параллельно запускаются файлы тестов. Тесты в одном файле запускаются по порядку в одном и том же рабочем процессе.
- Вы можете настроить тесты с помощью
test.describe.configure, чтобы запускать тесты в одном файле параллельно. - Вы можете настроить весь проект так, чтобы все тесты во всех файлах запускались параллельно, используя testProject.fullyParallel или testConfig.fullyParallel.
- Чтобы отключить параллельное выполнение, ограничьте количество рабочих процессов одним.
Вы можете управлять количеством параллельных рабочих процессов и ограничить число сбоев во всём наборе тестов для повышения эффективности.
Рабочие процессы
Все тесты выполняются в рабочих процессах. Это процессы ОС, работающие независимо друг от друга и управляемые средством запуска тестов. Среды всех рабочих процессов идентичны, и каждый из них запускает собственный браузер.
Рабочие процессы не могут обмениваться данными друг с другом. Playwright Test повторно использует один рабочий процесс, когда это возможно, чтобы ускорить тестирование, поэтому несколько файлов тестов обычно запускаются в одном рабочем процессе последовательно.
После сбоя теста рабочие процессы всегда завершаются, чтобы гарантировать чистую среду для последующих тестов.
Ограничение числа рабочих процессов
Вы можете управлять максимальным количеством параллельных рабочих процессов через командную строку или в файле конфигурации.
В командной строке:
npx playwright test --workers 4
В файле конфигурации:
import { defineConfig } from '@playwright/test';
export default defineConfig({
// Limit the number of workers on CI, use default locally
workers: process.env.CI ? 2 : undefined,
}); Отключение параллельного выполнения
Вы можете отключить параллельное выполнение, разрешив запуск только одного рабочего процесса одновременно. Для этого задайте параметр workers: 1 в файле конфигурации или передайте --workers=1 в командной строке.
npx playwright test --workers=1
Параллельный запуск тестов в одном файле
По умолчанию тесты в одном файле запускаются по порядку. Если в одном файле много независимых тестов, вы можете запускать их параллельно с помощью test.describe.configure().
Обратите внимание: параллельные тесты выполняются в отдельных рабочих процессах и не могут совместно использовать состояние или глобальные переменные. Для каждого теста отдельно выполняются все необходимые хуки, включая beforeAll и afterAll.
import { test } from '@playwright/test';
test.describe.configure({ mode: 'parallel' });
test('runs in parallel 1', async ({ page }) => { /* ... */ });
test('runs in parallel 2', async ({ page }) => { /* ... */ }); Также можно включить полностью параллельный режим для всех тестов в файле конфигурации:
import { defineConfig } from '@playwright/test';
export default defineConfig({
fullyParallel: true,
}); Кроме того, полностью параллельный режим можно включить только для нескольких проектов:
import { defineConfig } from '@playwright/test';
export default defineConfig({
// runs all tests in all files of a specific project in parallel
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
fullyParallel: true,
},
]
}); Последовательный режим
Вы можете пометить взаимозависимые тесты как последовательные. Если один из таких тестов завершается с ошибкой, все последующие тесты пропускаются. Все тесты в группе запускаются повторно вместе.
примечаниеНе рекомендуется использовать последовательный режим. Обычно лучше делать тесты изолированными, чтобы их можно было запускать независимо друг от друга.
import { test, type Page } from '@playwright/test';
// Annotate entire file as serial.
test.describe.configure({ mode: 'serial' });
let page: Page;
test.beforeAll(async ({ browser }) => {
page = await browser.newPage();
});
test.afterAll(async () => {
await page.close();
});
test('runs first', async () => {
await page.goto('https://playwright.dev/');
});
test('runs second', async () => {
await page.getByText('Get Started').click();
}); Блокировки тестов
Некоторые тесты в вашем наборе могут обращаться к общему ресурсу, который не поддерживает одновременный доступ, например к внешней службе или глобальной настройке аккаунта. Для таких тестов можно объявить именованные блокировки: тесты с одинаковым именем блокировки не будут выполняться одновременно, а все остальные тесты продолжат выполняться параллельно. Блокировки действуют между файлами, рабочими процессами и проектами.
import { test, expect } from '@playwright/test';
test('update user settings', { lock: 'user-settings' }, async ({ page }) => {
// ...
}); import { test, expect } from '@playwright/test';
// Never runs concurrently with 'update user settings' above,
// even in a different project.
test('rename user', { lock: 'user-settings' }, async ({ page }) => {
// ...
}); Тест может объявить несколько блокировок и будет запущен, только когда все они станут доступны. Блокировки также можно объявить для группы с помощью test.describe(); они будут применены ко всем тестам внутри группы.
test('reset the database', { lock: ['database', 'external-api'] }, async () => {
// ...
}); Playwright захватывает все блокировки теста до его запуска и освобождает их по завершении.
примечаниеВ режимах по умолчанию и последовательном все тесты в файле выполняются вместе по порядку, поэтому блокировка, объявленная для любого теста, удерживается на протяжении всего выполнения файла.
Отключение полностью параллельного режима
Если в конфигурации параллельный режим применяется ко всем тестам с помощью testConfig.fullyParallel, некоторые тесты всё же можно запускать с настройками по умолчанию. Вы можете переопределить режим для отдельного блока describe:
test.describe('runs in parallel with other describes', () => {
test.describe.configure({ mode: 'default' });
test('in order 1', async ({ page }) => {});
test('in order 2', async ({ page }) => {});
}); Как избежать общего состояния в параллельных тестах
Playwright запускает тесты в отдельных рабочих процессах, каждый из которых использует собственный изолированный BrowserContext, поэтому файлы cookie, хранилище и глобальные переменные в памяти уже изолированы. Нестабильность возникает из-за состояния, которое существует за пределами отдельного теста. Ниже приведены решения для типичных случаев.
Выделите каждому тесту собственные данные на сервере
Два теста, создающие или изменяющие одну и ту же запись, будут конфликтовать. Создавайте уникальный идентификатор на основе testInfo.testId, чтобы параллельные тесты не пересекались:
import { test, expect } from '@playwright/test';
test('creates an order', async ({ page }, testInfo) => {
const orderId = `order-${testInfo.testId}`;
await page.goto(`/orders/new?id=${orderId}`);
await expect(page.getByText(orderId)).toBeVisible();
}); Если многие тесты могут использовать один и тот же набор данных, создавайте его один раз для каждого рабочего процесса — см. раздел Изоляция тестовых данных между параллельными рабочими процессами.
Записывайте данные по уникальному пути к файлу
Если несколько тестов записывают данные по одному и тому же пути, они перезаписывают результаты друг друга. Метод testInfo.outputPath() возвращает путь, выделенный для текущего теста:
import { test } from '@playwright/test';
import fs from 'fs';
test('exports a CSV', async ({ page }, testInfo) => {
const file = testInfo.outputPath('export.csv');
await fs.promises.writeFile(file, 'a,b,c', 'utf8');
}); Сохраняйте независимость тестов
Прежде всего, обеспечьте изоляцию тестов друг от друга. Тест, который оставляет состояние в переменной уровня модуля или зависит от побочных эффектов другого теста, работает при последовательном запуске, но перестаёт работать, когда тесты выполняются параллельно или в другом порядке. Настраивайте всё необходимое для теста в самом тесте или в фикстуре и никогда не рассчитывайте на то, что другой тест уже был выполнен.
Распределение тестов по нескольким машинам
Playwright Test может распределить набор тестов по шардам, чтобы запускать его на нескольких машинах. Подробнее см. в руководстве по распределению тестов по шардам.
npx playwright test --shard=2/3
Ограничение числа сбоев и быстрый выход при ошибке
Вы можете ограничить число сбоев во всём наборе тестов, задав параметр конфигурации maxFailures или передав флаг командной строки --max-failures.
Если задано «максимальное число сбоев», Playwright Test остановится, достигнув этого числа, и пропустит все ещё не выполненные тесты. Это помогает не тратить ресурсы на сломанные наборы тестов.
Передача параметра командной строки:
npx playwright test --max-failures=10
Настройка в файле конфигурации:
import { defineConfig } from '@playwright/test';
export default defineConfig({
// Limit the number of failures on CI to save resources
maxFailures: process.env.CI ? 10 : undefined,
}); Индекс рабочего процесса и параллельный индекс
Каждому рабочему процессу присваиваются два идентификатора: уникальный индекс рабочего процесса, начинающийся с 1, и параллельный индекс в диапазоне от 0 до workers - 1. При перезапуске рабочего процесса, например после сбоя, новый рабочий процесс получает тот же parallelIndex и новый workerIndex.
Индекс можно получить из переменных среды process.env.TEST_WORKER_INDEX и process.env.TEST_PARALLEL_INDEX или обратиться к ним через testInfo.workerIndex и testInfo.parallelIndex.
Изоляция тестовых данных между параллельными рабочими процессами
Чтобы изолировать пользовательские данные в базе данных между тестами, выполняемыми в разных рабочих процессах, можно использовать упомянутый выше process.env.TEST_WORKER_INDEX или testInfo.workerIndex. Все тесты, выполняемые одним рабочим процессом, повторно используют одного и того же пользователя.
Создайте файл playwright/fixtures.ts, который будет создавать фикстуру dbUserName и инициализировать нового пользователя в тестовой базе данных. Используйте testInfo.workerIndex, чтобы различать рабочие процессы.
import { test as baseTest, expect } from '@playwright/test';
// Import project utils for managing users in the test database.
import { createUserInTestDatabase, deleteUserFromTestDatabase } from './my-db-utils';
export * from '@playwright/test';
export const test = baseTest.extend<{}, { dbUserName: string }>({
// Returns db user name unique for the worker.
dbUserName: [async ({ }, use) => {
// Use workerIndex as a unique identifier for each worker.
const userName = `user-${test.info().workerIndex}`;
// Initialize user in the database.
await createUserInTestDatabase(userName);
await use(userName);
// Clean up after the tests are done.
await deleteUserFromTestDatabase(userName);
}, { scope: 'worker' }],
}); Теперь каждый файл тестов должен импортировать test из файла с фикстурами вместо @playwright/test.
// Important: import our fixtures.
import { test, expect } from '../playwright/fixtures';
test('test', async ({ dbUserName }) => {
// Use the user name in the test.
}); Управление порядком выполнения тестов
Playwright Test запускает тесты в одном файле в порядке их объявления, если только вы не запускаете тесты в одном файле параллельно.
Порядок выполнения тестов в разных файлах не гарантируется, поскольку по умолчанию Playwright Test запускает файлы тестов параллельно. Однако если вы отключите параллельное выполнение, то сможете управлять порядком тестов, задав имена файлов в алфавитном порядке или используя файл «списка тестов».
Сортировка файлов тестов по алфавиту
Если вы отключите параллельный запуск тестов, Playwright Test будет запускать файлы тестов в алфавитном порядке. Для управления порядком можно использовать соглашение об именовании, например 001-user-signin-flow.spec.ts, 002-create-new-document.spec.ts и так далее.
Использование файла «списка тестов»
предупреждениеИспользование списков тестов не рекомендуется и поддерживается только в меру возможностей. Некоторые функции, такие как расширение VS Code и трассировка, могут работать со списками тестов некорректно.
Тесты можно разместить во вспомогательных функциях в нескольких файлах. Рассмотрим следующий пример: тесты определены не непосредственно в файле, а в функции-обёртке.
import { test, expect } from '@playwright/test';
export default function createTests() {
test('feature-a example test', async ({ page }) => {
// ... test goes here
});
}
import { test, expect } from '@playwright/test';
export default function createTests() {
test.use({ viewport: { width: 500, height: 500 } });
test('feature-b example test', async ({ page }) => {
// ... test goes here
});
} Можно создать файл списка тестов, который задаёт порядок запуска: сначала запустить тесты feature-b, затем тесты feature-a. Обратите внимание, что каждый файл тестов обёрнут в блок test.describe(), вызывающий функцию, в которой определены тесты. Таким образом, вызовы test.use() влияют только на тесты из одного файла.
import { test } from '@playwright/test';
import featureBTests from './feature-b.spec.ts';
import featureATests from './feature-a.spec.ts';
test.describe(featureBTests);
test.describe(featureATests); Теперь отключите параллельный запуск, задав число рабочих процессов равным единице, и укажите файл списка тестов.
import { defineConfig } from '@playwright/test';
export default defineConfig({
workers: 1,
testMatch: 'test.list.ts',
}); примечаниеНе определяйте тесты непосредственно во вспомогательном файле. Это может привести к непредвиденным результатам, поскольку тесты будут зависеть от порядка операторов
import/require. Вместо этого оберните тесты в функцию, которую будет явно вызывать файл списка тестов, как в примере выше.
© 2026 Microsoft
Licensed under the Apache License, Version 2.0.
https://playwright.dev/docs/test-parallel