Modern Browser Testing and Sharding with Pest 4
Overview of Pest 4 Capabilities
represents a massive shift in how developers approach end-to-end testing. Historically, browser testing in the ecosystem relied on , which often felt slow or difficult to debug in CI environments. has rebuilt the browser testing experience on top of , the industry standard for high-performance automation. This update brings lightning-fast execution, parallelization, and a suite of high-level assertions that make testing feel like an extension of unit testing rather than a separate, clunky chore.
Prerequisites and Environment Setup
To follow this tutorial, you should have a solid grasp of 8.2+ and fundamentals. Since the new browser testing engine utilizes , you will need installed to handle the underlying browser drivers. You should also be comfortable running terminal commands and managing a standard testing environment with .
Key Libraries & Tools
- : The core testing framework for .
- : The high-performance engine driving the browser interactions.
- : The application framework used for the live examples.
- : Used in the demo to show how handles and heavy front-ends.
Browser Testing Walkthrough
The syntax for browser testing in is remarkably clean. Instead of the standard get() request used in feature tests, you use visit(). This triggers the engine to render the page fully.
test('user can login', function () {
$user = User::factory()->create();
$this->visit('/')
->click('Login')
->type('email', $user->email)
->type('password', 'password')
->press('Login')
->assertSee('Dashboard');
});
One of the most impressive features is the shared memory state. In older tools, your browser and your test run in separate processes, making it hard to use RefreshDatabase. allows you to use in-memory across both the test and the browser, drastically increasing speed. Furthermore, you can mix unit testing helpers directly with browser actions. For instance, you can use Notification::fake() and then verify a notification was sent after a browser click—all within the same test block.
Advanced Debugging and Visual Diffs
Debugging browser tests has historically been a "guess and check" process. introduces the debug() and tinker() methods to stop this cycle. Placing ->debug() before a failing assertion pauses the test and focuses the browser window so you can see exactly what is wrong. The ->tinker() method is even more powerful; it opens a session at the exact moment of execution, allowing you to inspect the backend database state or the currently authenticated user.
Visual Regression Testing
Visual testing is now a first-class citizen. By calling assertScreenshotMatches(), captures a baseline image. On subsequent runs, it compares the current UI against that baseline.
test('homepage visual regression', function () {
$this->visit('/')
->assertScreenshotMatches();
});
If a single pixel is out of place due to a change, the test fails. You can run the test with the --diff flag to open a side-by-side slider in the browser, showing exactly what changed in red.
Syntax Notes and Modern Features
leans heavily into fluent chaining and "smoke testing" shortcuts. The assertNoSmoke() method is a powerful shorthand that automatically visits all routes in your application and asserts that there are no errors and no console.log statements left behind.
Another syntax feature is the device configuration. You can easily test how your site looks on different viewports by chaining methods like onMobile() or specifying exact hardware like onIphone15(). You can even toggle onDarkMode() to ensure your variables are rendering correctly across themes.
Sharding for GitHub Actions
As test suites grow, execution time often becomes a bottleneck. introduces sharding to solve this. Instead of one long 10-minute run, you can split your suite into smaller chunks across multiple runners. By adding the --shard=1/5 flag, you tell to only run a specific fifth of the tests. When combined with matrices, this can reduce a 10-minute CI process down to just 2 minutes without changing a single line of test code.
Tips & Gotchas
- Database Isolation: Always use the
RefreshDatabasetrait when testing browser flows that modify data. Because shares the connection, your database state stays in sync. - Automatic Waiting: You no longer need to manually code
waitForText(). automatically waits for redirects and element visibility, reducing flakiness. - Parallel Execution: Use the
--parallelflag locally to maximize your CPU cores. and both support parallel execution, often making browser tests run five times faster than sequential runs.
- 27%· products
- 15%· libraries
- 12%· frameworks
- 12%· products
- 9%· programming languages
- Other topics
- 24%

Pest 4: Modern Browser Testing, Sharding, Visual Diffs & more | Nuno Maduro at Laracon US 2025
WatchLaravel // 29:49
The official YouTube channel of Laravel, the clean stack for Artisans and agents. We will update you on what's new in the world of Laravel, from the framework to our products Cloud, Forge, and Nightwatch.