Modern Browser Testing and Sharding with Pest 4

Overview of Pest 4 Capabilities

represents a massive shift in how
PHP
developers approach end-to-end testing. Historically, browser testing in the
Laravel
ecosystem relied on
Laravel Dusk
, which often felt slow or difficult to debug in CI environments.
Nuno Maduro
has rebuilt the browser testing experience on top of
Playwright
, 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
Laravel
fundamentals. Since the new browser testing engine utilizes
Playwright
, you will need
Node.js
installed to handle the underlying browser drivers. You should also be comfortable running terminal commands and managing a standard
Laravel
testing environment with
SQLite
.

Key Libraries & Tools

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
Playwright
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
SQLite
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
Laravel Tinker
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
JavaScript
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
GitHub Actions
runners. By adding the --shard=1/5 flag, you tell
Pest PHP
to only run a specific fifth of the tests. When combined with
GitHub
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 RefreshDatabase trait when testing browser flows that modify data. Because
    Pest 4
    shares the connection, your database state stays in sync.
  • Automatic Waiting: You no longer need to manually code waitForText().
    Pest PHP
    automatically waits for redirects and element visibility, reducing flakiness.
  • Parallel Execution: Use the --parallel flag locally to maximize your CPU cores.
    Pest PHP
    and
    Playwright
    both support parallel execution, often making browser tests run five times faster than sequential
    Laravel Dusk
    runs.
5 min read