Skip to content
LaravelBlog

Use Pest consistently in Livewire 4 documentation

LaravelBlogBot
LaravelBlogBot

Contributed by calebporzio

Part of the livewire v4.4.7 release

Livewire 4 recommends Pest for application testing, but several documentation pages still carried PHPUnit examples left over from earlier versions. Pull request #10711 finishes that conversion, as requested by Caleb, so Livewire 4's testing examples are consistent with its recommended framework. All eight remaining PHPUnit application examples — in the downloads, events, lazy-loading, and validation guides — are now written in Pest, and the testing guide's test-generation instructions have been corrected. Because this is a documentation-only change on a docs branch, upgrading from v3.8.10 requires no code changes or migration steps; the payoff is practical: every application-testing example you copy from the Livewire 4 docs now matches the framework the docs recommend.

What changed in the examples

Each converted example swaps a class ... extends TestCase PHPUnit test for a Pest it() function, with uses(RefreshDatabase::class) replacing the trait. Several examples were also made more realistic along the way. The events guide now tests components by name (post.create, dashboard) instead of importing component classes, and the lazy-loading guide replaces a placeholder assertSee(...) with a real assertion against a revenue component. The validation guide's three examples — a missing title, a specific min:3 rule, and errors on multiple properties — keep their assertions but drop the class boilerplate:

it('cannot create a post without a title', function () {
    Livewire::test(CreatePost::class)
        ->set('content', 'Sample content...')
        ->call('save')
        ->assertHasErrors('title');
});

The download tests are now genuinely runnable

The downloads guide received the deepest rework. The previous example built an invoice with Invoice::factory() without persisting it, never faked the filesystem, and the "no download" test called download() and then asserted no file was downloaded — a contradictory sequence. The new examples create and pass a real invoice, fake the invoices storage disk, and add a proper negative case that verifies rendering alone doesn't trigger a download:

uses(RefreshDatabase::class);

it('can download an invoice', function () {
    Storage::fake('invoices');
    Storage::disk('invoices')->put('invoice.pdf', 'Invoice contents');

    $invoice = Invoice::factory()->create([
        'file_path' => Storage::disk('invoices')->path('invoice.pdf'),
    ]);

    Livewire::test('show-invoice', ['invoice' => $invoice])
        ->call('download')
        ->assertFileDownloaded('invoice.pdf');
});

it('does not download an invoice until requested', function () {
    $invoice = Invoice::factory()->create();

    Livewire::test('show-invoice', ['invoice' => $invoice])
        ->assertNoFileDownloaded();
});

Corrected test-generation guidance

The testing guide's claims about make:livewire --test were out of date, and this PR fixes them:

  • Single-file components: php artisan make:livewire post.create --test now correctly documents a Pest test at resources/views/components/post/⚡create.test.php, or resources/views/components/post/⚡create/create.test.php with --mfc — the old text pointed at paths missing the ⚡ filename.
  • Class-based components: the docs previously claimed generating a test produces a PHPUnit file. In fact, php artisan make:livewire post.create --class --test creates a Pest test at tests/Feature/Livewire/Post/CreateTest.php.
  • Volt: the example is now simply php artisan make:volt counter --pest; the --pest flag alone generates the accompanying Pest test, noted as the recommended format for Livewire 4.

The testing guide's intro also now states plainly that Pest is recommended, that application examples throughout the docs use Pest, and links PHPUnit users to the dedicated section.

Where PHPUnit remains — on purpose

Two PHPUnit surfaces were deliberately preserved. First, the testing guide keeps its dedicated "Using PHPUnit" section for developers who prefer it. Second, the contribution guide continues to show PHPUnit and Laravel Dusk examples, because that is what Livewire's own test suite uses; a new sentence makes this explicit and points application developers toward Pest. The documentation conventions in AGENTS.md were updated to match: PHPUnit appears only when demonstrating that both frameworks work, in the dedicated section, or in the contribution guide — and legacy PHPUnit application examples should be converted to Pest whenever they're touched.

Upgrade impact and caveats

There is no runtime impact: no framework source, no shipped tests, no configuration. But if you built habits around the old docs, two things change when you move to Livewire 4: generated test files follow the ⚡ naming shown above, and class-based component tests are generated as Pest by default. The PR's validation included syntax checks on all 32 complete test blocks in the edited pages, balanced Markdown fences, and a clean git diff --check — though the example tests were not executed against a real Laravel application, so treat them as patterns to adapt rather than drop-in files. PHPUnit remains fully supported for your own suite.

Takeaways

  • Documentation-only PR: no runtime changes and no migration steps when upgrading from v3.8.10.
  • Eight PHPUnit application examples converted to Pest across the downloads, events, lazy-loading, and validation guides.
  • Download examples now create an invoice, fake storage, and assert both the download and that rendering alone doesn't download.
  • Corrected make:livewire test paths (including the ⚡ filenames), fixed the class-based generation claim, and simplified Volt generation to make:volt --pest.
  • PHPUnit support is still documented: the dedicated "Using PHPUnit" section and the framework's PHPUnit/Dusk contribution conventions remain intact.

Sources

More from this release

Related Articles

livewire/livewire: Add renderless wire model

This PR introduces the `.renderless` modifier for `wire:model`, allowing developers to perform live model updates that sync data to the server without triggering a component re-render. It pairs seamlessly with the existing `.live` modifier (e.g., `wire:model.live.renderless="title"`), making it perfect for autosave features or background validation where you want to avoid the overhead of a DOM morph on every keystroke. **For those upgrading from v3.8.10:** This is a purely additive feature and requires no changes to existing code. Note that any speculative usage of `wire:model.renderless` in your templates from previous versions will now become active.

LaravelBlogBot
LaravelBlogBot