skip to content

Rendered State Assertions

Livewire::test() mounts a component without a browser so a test can set properties, call actions and assert on state, events and errors. Interviewers probe what such a test can and cannot prove.

on this pageshow

explore

questions

4

How do you test a Livewire newsletter sign-up component with Livewire::test(), set(), call() and assertSet()?

level: juniorimportance: must knowfreq 55%

answer

  1. Livewire::test() renders without a browser
  2. second argument goes to mount()
  3. each set() and call() is a round trip
  4. assertSet compares loosely; assertSetStrict
  5. assertSee escapes HTML by default

basics

~10 s

Livewire::test('newsletter-signup') renders the component in the test, set('email', ...) updates a property and call('subscribe') runs an action, each as a simulated Livewire request; then assertSet, assertSee and database assertions check the result.

solid answer

~40 s

`Livewire::test()` takes a component name or class, plus an optional array passed to `mount()`, renders it through Livewire's own request cycle without a browser, and returns a chainable `Testable`. `set('email', '[email protected]')` sends a property update and `call('subscribe')` an action call, each as a real round trip of the signed snapshot, so hydration, update hooks and validation run as they would in production. `toggle('consent')` flips a boolean. Assert on state with `assertSet('subscribed', true)`, which compares loosely like `assertEquals`, or `assertSetStrict()` for `assertSame`; on output with `assertSee()`, which HTML-escapes the expected text; and on side effects with ordinary Laravel assertions such as `assertDatabaseHas()`. The same API works in Pest and PHPUnit.

code

php · 15 lines
php
<?php

use App\Livewire\NewsletterSignup;
use Livewire\Livewire;

it('subscribes a new address', function () {
    Livewire::test(NewsletterSignup::class)
        ->set('email', '[email protected]')
        ->toggle('consent')
        ->call('subscribe')
        ->assertSet('subscribed', true)
        ->assertSee('Check your inbox');

    $this->assertDatabaseHas('subscribers', ['email' => '[email protected]']);
});

go deeper

for a junior

Remember the chain: Livewire::test(Component::class), then set(), call() and assertSet() or assertSee(), plus normal database assertions.

for a middle

Explain that each set() and call() is a simulated request with hydration, hooks and validation, and know the loose versus strict and escaping defaults.

for a senior

Write tests that pin behaviour rather than markup, choose strict assertions where types matter, and keep them fast enough to run on every change.

for a principal

Decide how component tests, HTTP tests and browser tests share the testing budget for a Livewire-heavy app, and what each layer must prove.

## What `Livewire::test()` is A **Livewire component test** exercises one component in isolation, in PHP, with no browser. `Livewire::test()` accepts: - a component **name** (`'newsletter-signup'`, or a dotted name for nested folders) or a **class** (`NewsletterSignup::class`); - an optional **array of parameters** passed to `mount()`, the way a parent component or route would pass them. It performs the initial render and returns a `Livewire\Features\SupportTesting\Testable`, a fluent object whose methods all return `$this`, so a test reads as one chain. ## Driving the component | Method | What it simulates | |---|---| | `set('email', '[email protected]')` | A property update, as `wire:model` would send | | `set(['email' => '...', 'name' => '...'])` | Several updates, applied one by one | | `toggle('consent')` | `set('consent', ! current value)` | | `call('subscribe')` / `call('remove', $id)` | An action call with parameters | | `refresh()` | A request with no updates or calls | Each of these is a **full round trip**: the test takes the last snapshot, posts it to Livewire's update endpoint inside the test application with the update or call, and records the new HTML, snapshot and effects. That matters: hydration and dehydration really happen, so a property type Livewire cannot serialize fails here, `updated*` hooks and `#[Validate]` real-time validation run, and a `#[Locked]` property throws `CannotUpdateLockedPropertyException` when you `set()` it. ## Asserting - **State**: `assertSet('subscribed', true)` reads the property from the component instance. It compares with `assertEquals`, so `'1'` equals `1`; use `assertSetStrict()` when the type matters. A closure works too: `assertSet('tags', fn ($tags) => count($tags) === 2)`. `assertNotSet()` and `assertCount('tags', 2)` complete the set. - **Output**: `assertSee('Thanks for subscribing')` searches the rendered HTML, with Livewire's `wire:snapshot` data stripped, and **escapes** the expected string first, so `assertSee('Tom & Jerry')` looks for `Tom &amp; Jerry`. Pass `false` as the second argument, or use `assertSeeHtml()`, to match raw markup. `assertDontSee()` is the negative. - **View data**: `assertViewHas('plans', ...)` checks what `render()` passed to the view. - **Side effects**: the component ran inside your test application, so ordinary Laravel assertions such as `assertDatabaseHas('subscribers', ...)` apply afterwards. ## A sign-up test ```php it('subscribes a new address', function () { Livewire::test(NewsletterSignup::class) ->set('email', '[email protected]') ->toggle('consent') ->call('subscribe') ->assertSet('subscribed', true) ->assertSee('Check your inbox'); $this->assertDatabaseHas('subscribers', ['email' => '[email protected]']); }); ``` ## Reading the component back Besides assertions, the `Testable` exposes the component for inspection: - `get('email')`, or simply `$component->email`, reads a property from the component **instance** after the last request, which is also what `assertSet()` uses. - `assertSnapshotSet('email', ...)` checks the **dehydrated** value instead, the JSON that would go to the browser. The two differ for synthesized types: an enum is an object on the instance but its backing value in the snapshot, and a model property's snapshot data is empty, because only its class and key travel, as metadata. - `instance()` returns the component object itself; `html()` returns the last rendered HTML; `assertReturned($value)` checks what the last action returned. - `dump()` and `dd()` print the last HTML when a test fails for reasons you cannot see. Prefer assertions on behaviour, such as properties, events and database rows, over long HTML fragments; markup changes more often than the rules it displays. ## Where the test file lives In Livewire 4, `php artisan make:livewire newsletter-signup --test` generates a Pest test next to a single-file component (`⚡newsletter-signup.test.php`); for a class-based component it goes under `tests/Feature/Livewire`. The Livewire docs recommend **Pest**, while the Laravel 13 skeleton ships **PHPUnit**; every `Testable` method works identically in both. Tests stored beside components need `resources/views` added to Pest's and PHPUnit's test paths. ## Common mistakes 1. Asserting on `$component->email` after a `set()` without realising `set()` already round-tripped; the property reflects the server's view, after hooks. 2. Using `assertSee()` with HTML in the expected string and wondering why escaped text never matches. 3. Expecting `assertSet()` to catch a type change from `int` to `string`; it is loose unless you use the strict variant. 4. Forgetting that `set()` works for properties no input binds, so a green test does not prove the form has a field for it.

  • In a Livewire test, what is the difference between assertSet('count', '3') and assertSetStrict('count', '3') when the property holds the integer 3?
    `assertSet()` compares with `assertEquals`, so the string `'3'` and the integer `3` are equal and it passes. `assertSetStrict()` uses `assertSame`, which also compares types, so it fails. Use the strict form when a type change, for example from a cast or a typed property, is part of what you are testing.
  • Why does a Livewire test fail when you set() a #[Locked] property, even though PHP code can change it?
    `set()` sends a real property update through Livewire's update cycle, exactly like the browser would. The locked attribute rejects client updates by throwing `CannotUpdateLockedPropertyException`, and in a test that exception reaches your test method. To put a locked property in a given state, pass it to `mount()` through the second argument of `Livewire::test()`.

saying these in an interview costs you the question

  • Livewire::test() needs a browser or a running server.
  • set() writes the property directly without running update hooks.
  • assertSet() compares with strict type checking by default.
  • assertSee() matches raw HTML unless told otherwise.
  • Livewire component tests only work with Pest, not PHPUnit.
open as a page

In a Livewire component test, how do assertHasErrors() and assertDispatched() check validation failures and events, and what do their arguments match?

level: middleimportance: must knowfreq 45%

basics

~10 s

assertHasErrors('email') checks the field has an error, and assertHasErrors(['email' => 'email']) that a specific rule or message failed. assertDispatched('subscribed') checks an event name, and named arguments check only the parameters you list.

open as a page

How do you test a Livewire sign-up component that depends on the logged-in user, a ?ref= query parameter and an uploaded CSV of contacts?

level: middleimportance: should knowfreq 30%

basics

~10 s

Chain Livewire::actingAs($member) and Livewire::withQueryParams(['ref' => 'footer']) before test(), then set('contacts', UploadedFile::fake()->create('contacts.csv', 4, 'text/csv')); Livewire simulates the whole temporary-upload flow for you.

open as a page

A Livewire::test() suite is green but the sign-up form breaks in the browser - what can a component test not prove, and what covers the gap?

level: seniorimportance: should knowfreq 35%

basics

~20 s

Livewire::test() runs the component's PHP round trips with route middleware disabled and no browser, so it misses JavaScript, Alpine, wire:model modifiers, missing inputs, routing and middleware. An assertSeeLivewire HTTP test and a few browser tests cover those.

open as a page