> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/microsoft/playwright/llms.txt
> Use this file to discover all available pages before exploring further.

# Parallelization

> Learn how to run Playwright tests in parallel for faster execution

## Introduction

Playwright Test runs tests in parallel by default using multiple worker processes. This significantly reduces test execution time.

## How Parallelization Works

From `src/common/config.ts:121,240-252` and `src/runner/testRunner.ts:88-115`:

Playwright uses a **worker-based parallelization model**:

1. Test runner spawns multiple worker processes
2. Each worker runs tests sequentially
3. Workers run in parallel to each other
4. Tests are distributed across available workers

```typescript theme={null}
export default defineConfig({
  workers: 4, // Run 4 tests in parallel
});
```

## Worker Configuration

### Fixed Number of Workers

```typescript theme={null}
export default defineConfig({
  workers: 4, // Always use 4 workers
});
```

### Percentage of CPU Cores

From `src/common/config.ts:240-252`:

```typescript theme={null}
export default defineConfig({
  workers: '50%', // Use 50% of available CPU cores
});

// Implementation:
function resolveWorkers(workers: string | number): number {
  if (typeof workers === 'string' && workers.endsWith('%')) {
    const cpus = os.cpus().length;
    return Math.max(1, Math.floor(cpus * (parseInt(workers, 10) / 100)));
  }
  return workers;
}
```

### Automatic Detection

```typescript theme={null}
export default defineConfig({
  workers: undefined, // Auto-detect based on CPU cores
});
```

### Environment-Based Configuration

```typescript theme={null}
export default defineConfig({
  workers: process.env.CI ? 2 : undefined,
  // Use 2 workers in CI, auto-detect locally
});
```

## Parallel Modes

From `src/common/test.ts:58` and `src/common/testType.ts:46-51,155-168`:

### Default Mode

Tests within a file run sequentially:

```typescript theme={null}
test('test 1', async ({ page }) => {});
test('test 2', async ({ page }) => {}); 
// test 2 waits for test 1 to complete
```

Different files run in parallel:

```bash theme={null}
tests/
  login.spec.ts      # Runs in worker 1
  checkout.spec.ts   # Runs in worker 2
  search.spec.ts     # Runs in worker 3
```

### Fully Parallel Mode

Run all tests in parallel:

From `src/common/config.ts:102`:

```typescript theme={null}
export default defineConfig({
  fullyParallel: true,
});
```

Or per-project:

```typescript theme={null}
projects: [
  {
    name: 'chromium',
    use: { browserName: 'chromium' },
    fullyParallel: true,
  },
]
```

### Describe Parallel

Make specific describe blocks run in parallel:

From `src/common/testType.ts:47,155-158`:

```typescript theme={null}
test.describe.parallel('Parallel suite', () => {
  test('test 1', async ({ page }) => {}); 
  test('test 2', async ({ page }) => {});
  test('test 3', async ({ page }) => {});
  // All three tests run in parallel
});
```

### Serial Mode

Force tests to run serially:

From `src/common/testType.ts:49,155-156`:

```typescript theme={null}
test.describe.serial('Serial suite', () => {
  test('test 1', async ({ page }) => {}); 
  test('test 2', async ({ page }) => {});
  // test 2 waits for test 1
});
```

<Warning>
  `describe.parallel` cannot be nested inside `describe.serial` or default mode describe blocks.
</Warning>

## Worker Isolation

Each worker maintains its own isolated environment:

### Worker-Scoped Fixtures

Shared within a worker, isolated between workers:

```typescript theme={null}
const test = base.extend({
  database: [async ({}, use) => {
    const db = await createDatabase();
    await use(db);
    await db.close();
  }, { scope: 'worker' }],
});

test('test 1', async ({ database }) => {
  // Uses same database as test 2 if in same worker
});

test('test 2', async ({ database }) => {
  // Reuses database from test 1 if in same worker
});
```

### Browser Instance Sharing

From `src/index.ts:104-126`:

Browser instances are shared within workers:

```typescript theme={null}
browser: [async ({ playwright, browserName }) => {
  const browser = await playwright[browserName].launch();
  await use(browser);
  await browser.close({ reason: 'Test ended.' });
}, { scope: 'worker' }]
```

**Each worker:**

* Launches one browser instance
* Creates fresh contexts for each test
* Reuses browser across tests

## Controlling Parallelization

### Project-Level Workers

From `src/common/config.ts:212-215`:

```typescript theme={null}
projects: [
  {
    name: 'setup',
    testMatch: /.*\.setup\.ts/,
    workers: 1, // Always use 1 worker for setup
  },
  {
    name: 'chromium',
    use: { browserName: 'chromium' },
    workers: 4, // Use 4 workers for chromium tests
  },
]
```

### Configure Mode

From `src/common/testType.ts:186-209`:

```typescript theme={null}
test.describe('Suite', () => {
  test.describe.configure({ mode: 'parallel' });
  
  test('test 1', async ({ page }) => {});
  test('test 2', async ({ page }) => {});
  // Both run in parallel
});

test.describe('Serial Suite', () => {
  test.describe.configure({ mode: 'serial' });
  
  test('test 1', async ({ page }) => {});
  test('test 2', async ({ page }) => {});
  // Run serially
});
```

## Worker Index

Access worker index in tests:

```typescript theme={null}
test('test', async ({ page }, testInfo) => {
  console.log(`Running in worker ${testInfo.workerIndex}`);
  
  // Use worker index for port allocation
  const port = 3000 + testInfo.workerIndex;
  await page.goto(`http://localhost:${port}`);
});
```

## Sharding

Distribute tests across multiple machines:

From `src/common/config.ts:116`:

### Configuration

```typescript theme={null}
export default defineConfig({
  shard: { total: 3, current: 1 },
});
```

### CLI Usage

```bash theme={null}
# Machine 1
npx playwright test --shard=1/3

# Machine 2
npx playwright test --shard=2/3

# Machine 3
npx playwright test --shard=3/3
```

### CI/CD Example

```yaml theme={null}
# GitHub Actions
strategy:
  matrix:
    shardIndex: [1, 2, 3, 4]
    shardTotal: [4]
steps:
  - name: Run tests
    run: npx playwright test --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
```

## Test Distribution

Tests are distributed to balance execution time:

```typescript theme={null}
// Playwright automatically distributes:
tests/
  fast-test.spec.ts       # 5 tests, 1s each   -> Worker 1
  medium-test.spec.ts     # 3 tests, 10s each  -> Worker 2
  slow-test.spec.ts       # 1 test, 30s        -> Worker 3
```

## Parallel Execution Patterns

### Independent Tests

Tests that don't share state:

```typescript theme={null}
export default defineConfig({
  fullyParallel: true, // All tests run in parallel
});

test('user login', async ({ page }) => {
  // Independent
});

test('product search', async ({ page }) => {
  // Independent
});
```

### Shared Setup with Parallel Tests

```typescript theme={null}
test.describe('E-commerce', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('/');
    await loginUser(page);
  });
  
  // These run in parallel if fullyParallel is true
  test('view cart', async ({ page }) => {});
  test('view wishlist', async ({ page }) => {});
  test('view orders', async ({ page }) => {});
});
```

### Sequential Test Flow

```typescript theme={null}
test.describe.serial('Checkout flow', () => {
  test('add to cart', async ({ page }) => {
    // Must run first
  });
  
  test('enter shipping', async ({ page }) => {
    // Depends on cart state
  });
  
  test('complete payment', async ({ page }) => {
    // Depends on shipping
  });
});
```

## Performance Optimization

### Optimal Worker Count

```typescript theme={null}
// Too few workers: underutilized CPU
workers: 1

// Optimal: balance CPU usage and overhead
workers: os.cpus().length

// Too many workers: excessive overhead
workers: os.cpus().length * 4
```

### Test Organization

```typescript theme={null}
// Good: Fast tests together
tests/
  unit/          # Fast tests, high parallelization
  integration/   # Medium tests, moderate parallelization  
  e2e/           # Slow tests, lower parallelization

// Configure per directory
projects: [
  { testDir: './tests/unit', workers: 8 },
  { testDir: './tests/integration', workers: 4 },
  { testDir: './tests/e2e', workers: 2 },
]
```

### Browser Reuse

Worker-scoped browser fixture reuses browsers:

```typescript theme={null}
// Efficient: Browser launched once per worker
test('test 1', async ({ browser }) => {
  const context = await browser.newContext();
  // ...
});

test('test 2', async ({ browser }) => {
  // Reuses same browser from test 1
});
```

## Debugging Parallel Tests

### Run Tests Serially

```bash theme={null}
npx playwright test --workers=1
```

### View Worker Output

```bash theme={null}
npx playwright test --workers=1 --reporter=line
```

### Test Info Debugging

```typescript theme={null}
test('debug info', async ({ page }, testInfo) => {
  console.log({
    workerIndex: testInfo.workerIndex,
    parallelIndex: testInfo.parallelIndex,
    retry: testInfo.retry,
  });
});
```

## Common Pitfalls

### Shared Mutable State

```typescript theme={null}
// BAD: Shared state between tests
let userId: string;

test('create user', async ({ request }) => {
  const response = await request.post('/users');
  userId = (await response.json()).id; // Race condition!
});

test('delete user', async ({ request }) => {
  await request.delete(`/users/${userId}`); // May run before create!
});

// GOOD: Independent tests
test('create user', async ({ request }) => {
  const response = await request.post('/users');
  const userId = (await response.json()).id;
  await request.delete(`/users/${userId}`);
});
```

### File System Operations

```typescript theme={null}
// BAD: Same file across tests
test('test 1', async () => {
  fs.writeFileSync('data.json', '{}'); // Conflict!
});

test('test 2', async () => {
  fs.writeFileSync('data.json', '{}'); // Conflict!
});

// GOOD: Worker-specific files
test('test 1', async ({ }, testInfo) => {
  const file = `data-${testInfo.workerIndex}.json`;
  fs.writeFileSync(file, '{}');
});
```

### Database Conflicts

```typescript theme={null}
// BAD: Shared database records
test('update user', async ({ request }) => {
  await request.put('/users/1', { name: 'New Name' });
});

test('delete user', async ({ request }) => {
  await request.delete('/users/1'); // Conflicts with update!
});

// GOOD: Unique records per test
test('update user', async ({ request }) => {
  const user = await createUniqueUser();
  await request.put(`/users/${user.id}`, { name: 'New Name' });
});
```

## Best Practices

1. **Design for parallelization**: Make tests independent
2. **Use appropriate worker count**: Balance speed and resources
3. **Avoid shared state**: Each test should be self-contained
4. **Use worker-scoped fixtures**: For expensive setup
5. **Leverage sharding**: For very large test suites
6. **Monitor CI resources**: Adjust workers based on available CPU
7. **Use serial mode sparingly**: Only when necessary

## Example: Optimal Configuration

```typescript theme={null}
import { defineConfig } from '@playwright/test';
import os from 'os';

export default defineConfig({
  // Use 50% of CPUs locally, 100% in CI
  workers: process.env.CI ? '100%' : '50%',
  
  // Enable full parallelization
  fullyParallel: true,
  
  // Limit failures to stop early
  maxFailures: process.env.CI ? 10 : undefined,
  
  projects: [
    {
      name: 'setup',
      testMatch: /.*\.setup\.ts/,
      workers: 1, // Setup runs serially
    },
    {
      name: 'chromium',
      dependencies: ['setup'],
      use: { browserName: 'chromium' },
      // Inherits workers from global config
    },
  ],
});
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Retries" icon="rotate" href="/test-runner/retries">
    Learn about test retries
  </Card>

  <Card title="Configuration" icon="gear" href="/test-runner/configuration">
    Configure worker settings
  </Card>
</CardGroup>
