Spec-Zone.ru › Playwright

Параллельное выполнение

Введение​

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

Spec-Zone.ru

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