</>

Technology

Playwright

Difficulty

Advanced

Interview Question

You have 500+ Playwright tests taking 45 minutes. How do you optimize to under 10 minutes?

Answer

Scenario: Optimizing 500 Tests from 45 Minutes to Under 10 Minutes

Current State Analysis

CODE
500 tests × 5.4 seconds avg = 45 minutes
Running sequentially (1 worker, no sharding)
Login UI for every test: 500 × 5s = 41 minutes wasted
Images/analytics loaded: adds 1-2s per navigation

Optimization Plan — Targeting <10 Minutes

Step 1: Enable Parallel Workers (Free 4x Speedup)

TypeScript
// playwright.config.ts
export default defineConfig({
  fullyParallel: true,
  workers: 8,  // 8 parallel browsers
});
CODE
500 tests / 8 workers = 62.5 tests per worker
62.5 × 5.4s = ~5.6 minutes

Step 2: storageState Auth Reuse (Eliminate Login Cost)

TypeScript
// Login ONCE per test run — not 500 times
projects: [
  { name: 'setup', testMatch: '*.setup.ts' },
  {
    name: 'e2e',
    use: {
      storageState: '.auth/user.json',   // Already logged in
    },
    dependencies: ['setup'],
  },
],
CODE
Before: 500 tests × 5s login = 41 minutes just on logins
After:  1 login = 5 seconds total
Savings: 40+ minutes

Step 3: Shard Across 8 CI Machines

YAML
# GitHub Actions matrix
strategy:
  matrix:
    shardIndex: [1, 2, 3, 4, 5, 6, 7, 8]
    shardTotal: [8]

steps:
  - run: npx playwright test --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
CODE
8 machines × 8 workers = 64 tests simultaneously
500 / 64 = ~8 tests per worker batch
8 × avg_duration ≈ target

Step 4: API Setup Instead of UI Flows

TypeScript
// BAD — UI-based test data setup: 5-8 seconds
test.beforeEach(async ({ page }) => {
  await page.goto('/products/new');
  await page.fill('#name', 'Test Product');
  await page.click('#save'); // 8s total
});

// GOOD — API setup: 200ms
test.beforeEach(async ({ request }) => {
  await request.post('/api/products', {
    data: { name: `Product-${Date.now()}` },
    headers: { Authorization: `Bearer ${process.env.ADMIN_TOKEN}` },
  }); // 200ms total
});

Step 5: Block Unnecessary Assets

TypeScript
// playwright.config.ts
use: {
  launchOptions: {
    args: ['--disable-extensions', '--no-sandbox'],
  },
},

// In fixture or beforeEach
await page.route('**/*.{png,jpg,jpeg,gif,svg,woff,woff2}', r => r.abort());
await page.route('**/google-analytics.com/**', r => r.abort());
await page.route('**/hotjar.com/**', r => r.abort());

Reduces each page load from 3s → 1.5s for asset-heavy pages.

Step 6: Use domcontentloaded Instead of networkidle

TypeScript
// BAD — waits for all network activity
await page.goto('/products', { waitUntil: 'networkidle' }); // 3-5s

// GOOD — wait for DOM ready, then for specific element
await page.goto('/products', { waitUntil: 'domcontentloaded' });
await page.locator('.product-list').waitFor(); // Specific wait

Results After All Optimizations

OptimizationTime Saved
8 workers (parallel)45m → 6m
8-way sharding6m → 45s
storageState auth41m saved total
API setup2-4s per test
Block assets20-40% per navigation
Combined result: 500 tests in ~6-8 minutes on CI

Follow AutomateQA

Related Topics