15 Days of Playwright - Day 9: Test Configuration & Parallel Execution
Learn how to configure Playwright for optimal performance and run tests in parallel to significantly reduce execution time.


Jhonatas Matos
After learning how to organize tests with Page Object Model, the next step is optimizing test execution. According to Playwright's documentation, proper configuration can improve test performance by 300% or more through parallel execution.
Today, I'll explore Playwright's configuration system and how to leverage parallel testing to dramatically reduce test suite execution time.
Understanding Playwright Configuration
The playwright.config.js
file is the heart of Playwright's test setup. The official documentation recommends this structure for managing all aspects of test execution, from timeouts to reporters.
Basic Configuration
This example shows the essential settings every Playwright configuration should include:
import { defineConfig } from '@playwright/test';
export default defineConfig({
// Global timeout for each test
timeout: 30000,
// Number of retries for failed tests
retries: process.env.CI ? 2 : 0,
// Maximum time for assertions to complete
expect: {
timeout: 5000
},
// Reporters for test results
reporter: [
['html', { outputFolder: 'test-results/report' }],
['list'] // Simple console output
],
// Global setup and teardown
globalSetup: require.resolve('./global-setup'),
globalTeardown: require.resolve('./global-teardown')
});
Key configuration options explained:
timeout
: Maximum time for a single test to complete (prevents hanging tests)retries
: Automatic retry attempts for flaky tests (essential for CI)expect.timeout
: Specific timeout for assertions (shorter than test timeout)reporter
: How test results are displayed and storedglobalSetup/teardown
: Code that runs once before/after all tests
This basic configuration provides a solid foundation that can be extended with more advanced options as your test suite grows.
Parallel Test Execution
Playwright's parallel execution is one of its most powerful features. By default, tests run in parallel, but you can control the intensity and behavior through configuration.
Configuring Parallel Workers
Parallel workers are the key to faster test execution. Each worker runs tests in its own process, allowing multiple tests to run simultaneously.
export default defineConfig({
// Number of parallel workers
workers: process.env.CI ? 4 : 2,
// Or use percentage of CPU cores
// workers: '50%',
// Force tests to run in sequence
// workers: 1,
use: {
// Base URL for all tests
baseURL: process.env.BASE_URL || 'http://localhost:3000',
// Trace generation for debugging
trace: process.env.CI ? 'on-first-retry' : 'on',
// Video recording
video: process.env.CI ? 'on' : 'off',
// Screenshot on failure
screenshot: 'only-on-failure'
}
});
Parallelism strategies:
workers: process.env.CI ? 4 : 2
: Use more workers in CI (where you have more resources), fewer locallyworkers: '50%'
: Use half of available CPU cores (automatic scaling)workers: 1
: Force sequential execution (useful for debugging)
Environment-aware settings:
trace
: In CI, only trace on first retry to save resources. Locally, always trace for debuggingvideo
: Record videos in CI for failure analysis, disable locally for performancescreenshot
: Capture screenshots only when tests fail to reduce artifact size
Best practices:
- Start with
workers: '50%'
and adjust based on test performance - Use more workers for CPU-heavy CI environments
- Use fewer workers for memory-intensive tests
- Monitor test stability - too many workers can cause resource contention
This configuration can reduce test execution time by 70-80% compared to sequential runs.
Multi-Browser Configuration
Playwright supports testing across multiple browsers simultaneously through the projects
configuration. This allows you to run the same test suite against different environments in parallel.
Multiple Browser Projects
The projects
array lets you define different testing environments that run concurrently. Each project can have its own browser, device, and settings.
export default defineConfig({
projects: [
{
name: 'chromium',
use: {
browserName: 'chromium',
viewport: { width: 1280, height: 720 }
},
},
{
name: 'firefox',
use: {
browserName: 'firefox',
viewport: { width: 1280, height: 720 }
},
},
{
name: 'webkit',
use: {
browserName: 'webkit',
viewport: { width: 1280, height: 720 }
},
},
// Mobile emulation
{
name: 'Mobile Chrome',
use: {
browserName: 'chromium',
...devices['iPhone 12']
},
}
]
});
Key concepts:
projects
: Array of test environments that run in parallelname
: Unique identifier for each project (shown in reports)use
: Configuration specific to each projectbrowserName
: Which browser to use (chromium, firefox, webkit)viewport
: Consistent viewport size across browsersdevices
: Pre-defined mobile device configurations
Execution behavior:
- Each project runs in parallel with others
- Tests execute against all specified browsers simultaneously
- Results are aggregated in the final report
- You can run specific projects using
--project=project-name
Mobile testing made easy:
- Use
...devices['device-name']
to emulate mobile devices - Includes pre-configured viewport, user agent, and device metrics
- No need to manually configure mobile settings
Benefits of multi-project setup:
- Cross-browser testing without additional test code
- Parallel execution across different environments
- Consistent configuration for each browser/device
- Flexible execution - run all projects or specific ones
This approach ensures your application works correctly across all target browsers and devices with minimal configuration effort.
Environment-Specific Configuration
Playwright's configuration system allows you to create environment-aware settings that adapt to different execution contexts. This is essential for maintaining optimal performance across local development, staging, and production environments.
Different Settings per Environment
Using environment variables and conditional logic, you can tailor your configuration to each environment's specific needs.
export default defineConfig({
// Common settings
timeout: 30000,
// Environment-specific overrides
use: {
baseURL: process.env.BASE_URL || 'http://localhost:3000',
trace: process.env.CI ? 'on-first-retry' : 'retain-on-failure',
// Different viewports per environment
viewport: process.env.CI
? { width: 1920, height: 1080 }
: { width: 1280, height: 720 }
},
// Different retry strategies
retries: process.env.CI ? 2 : 0,
// Different reporters
reporter: process.env.CI ? 'html' : 'list'
});
Environment detection patterns:
process.env.CI
: Detects CI/CD environments (GitHub Actions, Jenkins, etc.)process.env.BASE_URL
: Custom environment variables for different deploymentsprocess.env.NODE_ENV
: Common Node.js environment indicator
Common environment-specific settings:
CI/CD Environment (process.env.CI = true):
retries: 2
: Automatic retries for flaky tests in unstable environments
trace: 'on-first-retry'
: Balance between debugging and performance
viewport: { width: 1920, height: 1080 }
: Desktop resolution for consistency
reporter: 'html'
: Comprehensive reports for CI systems
Local Development (process.env.CI = false):
retries: 0
: Immediate failure for quick feedback during development
trace: 'retain-on-failure'
: Detailed debugging info when needed
viewport: { width: 1280, height: 720 }
: Common development resolution
reporter: 'list'
: Simple console output for quick feedback
Benefits of environment-aware configuration:
- Optimized performance for each environment
- Better resource management in resource-constrained CI systems
- Improved debugging with appropriate detail levels
- Consistent behavior across different execution contexts
Setting environment variables:
# Local development
BASE_URL=http://localhost:3000 npx playwright test
# CI environment
CI=true BASE_URL=https://staging.example.com npx playwright test
# Production testing
CI=true BASE_URL=https://app.example.com npx playwright test
This approach ensures your tests run optimally in every environment while maintaining consistency and reliability.
Advanced Configuration Patterns
Playwright's project system enables sophisticated test organization through tagging and filtering. This allows you to create different test suites with customized configurations for specific purposes.
Tagging and Filtering Tests
You can use test tags to categorize tests and then create dedicated projects that only run tests with specific tags. This enables tailored execution environments for different test types.
export default defineConfig({
projects: [
{
name: 'smoke',
grep: /@smoke/,
use: {
browserName: 'chromium',
// Fast settings for smoke tests
timeout: 15000
}
},
{
name: 'regression',
grep: /@regression/,
use: {
browserName: 'chromium',
// Comprehensive settings for regression
trace: 'on',
video: 'on'
}
},
{
name: 'api',
grep: /@api/,
use: {
// API tests don't need browser
browserName: 'chromium',
// But can run headless for speed
headless: true
}
}
]
});
How tagging works:
grep
: Regular expression to match test names or tags@smoke
,@regression
,@api
: Convention for test categorization- Test-level tagging: Add tags to individual tests using comments or metadata
Example test tagging:
// In your test files:
test('@smoke Login with valid credentials', async ({ page }) => {
// Smoke test implementation
});
test('@regression @api User profile API tests', async ({ request }) => {
// API regression test
});
Project-specific configurations:
Smoke Tests (@smoke
):
timeout: 15000
: Shorter timeout for quick feedback
Fast execution: Minimal tracing and video
Critical path only: Focus on essential functionality
Run frequency: Before deployments, after major changes
Regression Tests (@regression
):
trace: 'on'
: Detailed tracing for debugging
video: 'on'
: Video recording for failure analysis
Comprehensive coverage: All functionality tested
Run frequency: Nightly, weekly schedules
API Tests (@api
):
headless: true
: No UI overhead for faster execution
Browser context: Still available for API calls that need cookies
Focus: Backend functionality without UI dependencies
Execution commands:
# Run only smoke tests
npx playwright test --project=smoke
# Run regression tests
npx playwright test --project=regression
# Run API tests
npx playwright test --project=api
# Run tests with specific tag across all projects
npx playwright test --grep="@smoke"
# Exclude tests with specific tag
npx playwright test --grep-invert="@slow"
Benefits of test tagging:
- Targeted execution: Run specific test suites when needed
- Customized configuration: Different settings for different test types
- Faster feedback: Quick smoke tests for rapid validation
- Resource optimization: Appropriate settings for each test category
- Better organization: Logical grouping of related tests
This pattern transforms your test suite from a monolithic entity into a modular, targeted testing system that can adapt to different needs and contexts.
Custom Test Directories
export default defineConfig({
// Test directory structure
testDir: './tests', // Directory containing test files
// File inclusion patterns
testMatch: '**/*.spec.js', // Only include .spec.js files
// File exclusion patterns
testIgnore: '**/*.test.js', // Exclude .test.js files
// Output directories
outputDir: 'test-results', // Test artifacts directory
snapshotDir: '__snapshots__' // Visual comparison snapshots
});
Best Practices for Parallel Execution
Parallel test execution requires careful configuration to avoid resource contention and ensure test isolation. Playwright's parallel execution model needs proper tuning for optimal performance.
Optimizing for Parallelism
This configuration demonstrates advanced patterns for maximizing parallel execution efficiency while maintaining test reliability.
export default defineConfig({
// Optimal worker count
workers: process.env.CI
? Math.min(8, require('os').cpus().length - 1)
: 3,
// Test isolation for parallel execution
use: {
// Each test gets its own storage state
storageState: undefined,
// Fresh context for each test
bypassCSP: true
}
});
Advanced Worker Configuration:
-
Math.min(8, require('os').cpus().length - 1)
: Dynamic CPU-based calculation -
require('os').cpus().length
: Gets actual CPU core count -
- 1
: Leaves one core free for system operations -
Math.min(8, ...)
: Caps maximum at 8 workers to avoid overloading -
process.env.CI ? ... : 3
: 3 workers locally for development comfort
Why dynamic worker calculation matters:
- Avoids overloading: Prevents using all CPU cores
- Adapts to hardware: Works on different CI machines
- Balances performance: Optimal worker count for each environment
Test Isolation Techniques:
storageState: undefined
: Ensures each test starts with clean authentication statebypassCSP: true
: Avoids content security policy issues in parallel execution
Additional Parallelism Optimizations:
// For even better parallel performance:
export default defineConfig({
// Prevent resource sharing between tests
fullyParallel: true,
// Fail fast on CI - don't run all tests if many are failing
forbidOnly: !!process.env.CI,
// Disable parallelization when debugging
workers: process.env.DEBUG ? 1 : undefined,
use: {
// Reduce memory usage per context
reducedMotion: 'reduce',
// Faster teardown for parallel contexts
serviceWorkers: 'block',
}
});
Monitoring Parallel Performance:
# See worker utilization
npx playwright test --workers=4 --reporter=line
# Test with different worker counts
npx playwright test --workers=2
npx playwright test --workers=4
npx playwright test --workers=6
# Measure execution time
time npx playwright test --workers=4
Signs of Optimal Parallelization:
✅ Tests complete 3-4x faster with parallel execution
✅ CPU utilization around 70-80% (not 100%)
✅ No test failures due to resource contention
✅ Consistent execution times across runs
Common Parallelism Issues to Avoid:
❌ Too many workers causing memory exhaustion
❌ Shared state between tests causing flakiness
❌ Resource contention (database, API rate limiting)
❌ Non-atomic tests that depend on execution order
This configuration ensures you get the maximum benefit from parallel execution while maintaining test stability and reliability.
CLI Commands for Configuration
Useful Execution Commands
# Run with specific number of workers
npx playwright test --workers=4
# Run specific project
npx playwright test --project=firefox
# Run tests with specific tag
npx playwright test --grep="@smoke"
# Run tests from specific file
npx playwright test tests/login.spec.js
# Run with custom timeout
npx playwright test --timeout=60000
# Run with trace enabled
npx playwright test --trace=on
Practical Implementation
Want to see all these configuration patterns working together in a real test suite? Check out the complete example on GitHub:
Link to GitHub project
Official Resources
- Playwright Configuration - Complete configuration guide
- Parallel Execution - Parallel testing documentation
- CLI Reference - Command line options
- Test Projects - Multi-browser setup
Conclusion
Proper test configuration and parallel execution are essential for modern test automation. Playwright's flexible configuration system allows you to:
- Reduce test execution time by 80-90% through parallelization
- Run tests across multiple browsers simultaneously
- Customize settings for different environments and needs
- Scale test suites to thousands of tests without slowing down
By leveraging these features, you can transform your test suite from a slow, sequential process into a fast, efficient testing pipeline.
Thank you for reading and see you in the next lesson! ☕💜