How to speed up Playwright tests
Runners
May 2, 2025
A guide to speeding up Playwright tests.
If your Playwright experience was like mine, it started out working well.
Our tests ran quick, issues were caught early, and test times were fast. But as the number of tests grew, the longer we had to wait on jobs. My team grew and before I knew it, CI pipelines were sluggish, pull requests were stuck waiting on test results, and everyone was frustrated at how long it took to merge and ship.
Fortunately, we’ve seen a few tricks to help frontend teams. I’ve distilled these findings down to a few concrete strategies I use to speed up tests without sacrificing coverage or reliability.
Here are the four key pillars I focus on when optimizing test suites:
Parallelization
Minimization
Optimization
Stabilization
Parallelization
Parallelizing your tests is a great first step—it spreads the workload across multiple worker processes, cutting down runtime and speeding up feedback loops.
Run Playwright Tests in Parallel
Playwright supports parallel test execution out of the box. To enable it, configure the number of workers in your playwright.config.ts
:
More workers mean faster execution but higher CPU and memory usage.
Duration-based test sharding
This one requires more effort but pays off in the long run. You can pull test durations from the JUnit XML report, and then shard by test duration so that each worker gets roughly equal total time instead of equal file count.
Leverage Playwright Browser Contexts Instead of New Browsers
Creating a new browser instance is slow. Instead, use browser contexts to isolate sessions efficiently:
Contexts allow faster test execution without incurring the cost of launching new browser instances
Minimization
Minimization is all about cutting out unnecessary overhead. Reducing extra processing, interactions, and dependencies is an easy way to speed things up and make tests more efficient. In frontend testing, every extra browser interaction adds time, so the goal here is to eliminate anything that doesn’t directly contribute to validating functionality.
Disable unneeded browser features in CI
Example:
Disable CSS animations/transitions globally
Adding the following snippet cuts out wait-for-animation overhead everywhere.
Use API Calls Instead of UI Interactions When Possible
For setting up test data, use API requests instead of navigating the UI:
Optimization
Optimization is about making the tests themselves run as efficiently as possible. This means reducing unnecessary execution, improving selector performance, and ensuring tests only do what's necessary to validate functionality.
Run Tests with Persistent Authentication
Re-authenticating for every test slows things down. Instead, persist authentication data across tests:
Reuse it in tests:
Optimize Locators and Selectors
Avoid using XPath selectors to test individual components. XPath is generally slow because it requires full DOM traversal to find elements, scanning each node, extracting text, and matching it, which is computationally expensive.
Replace brittle and slow selectors like //div[contains(text(),'Click me')]
with more efficient strategies:
Use data-testid
attributes:
Prefer getByRole when applicable:
Reduce Unnecessary Waits
Avoid using static wait statements like await page.waitForTimeout(5000);
. Instead, rely on Playwright’s built-in waiting mechanisms:
Stabilization
Stabilization is about making tests reliable. Flaky tests waste time and break trust in tests, so the goal is to remove sources of flakiness and ensure consistent, repeatable results.
Enable Tracing Selectively
Tracing is helpful for debugging but slows down tests. Enable it only when needed:
Use Test Retries to Avoid Unnecessary Reruns
Instead of rerunning an entire suite, configure retries to catch flakiness:
Retry segments within a test
You can use the toPass() assertion in conjunction with retries to make sure a code block successfully executes before proceeding. For example:
When the button is clicked, if the javascript hasn’t loaded yet, the popup will not be visible. By including ‘toPass’, Playwright will fail the inner block after a timeout and keep retrying until the javascript has loaded and test passes; or until the global test timeout.
Full Speed Ahead
These are just a few optimization strategies I’ve learned over the years and have used to speed up tests. In my experience, these investment compound as the team and codebase grows; they'll thank you for it later (and probably sooner).
Beyond just improving test speed, these optimizations will also
Improve developer experience and morale.
Reduce CI costs.
Save developer time waiting on slow tests.
Help teams ship features and products faster.
Now, I've taken these learnings even further through BuildPulse, a team dedicated to making tests run faster and more reliably. We currently offer BuildPulse Runners: Run your GitHub Actions jobs 2x fast, at half cost, with no tooling changes and BuildPulse Flaky Tests: a platform to detect, monitor, and contain test instability.
If you're tired of slow, buggy CI holding your team back, you can try BuildPulse for free!