Testing
Livewire components are simple to test. Because they are just Laravel classes under the hood, they can be tested using Laravel's existing testing tools. However, Livewire provides many additional utilities to make testing your components a breeze.
This documentation will guide you through testing Livewire components using Pest as the recommended testing framework, though you can also use PHPUnit if you prefer.
Installing Pest
Pest is a delightful PHP testing framework with a focus on simplicity. It's the recommended way to test Livewire components in Livewire 4.
To install Pest in your Laravel application, first remove PHPUnit (if it's installed) and require Pest:
composer remove phpunit/phpunitcomposer require pestphp/pest --dev --with-all-dependencies
Next, initialize Pest in your project:
./vendor/bin/pest --init
This will create a tests/Pest.php configuration file in your project.
For more detailed installation instructions, see the Pest installation documentation.
Configuring Pest for view-based components
If you're writing tests alongside your view-based components (single-file or multi-file), you'll need to configure Pest to recognize these test files.
First, update your tests/Pest.php file to include the resources/views directory:
pest()->extend(Tests\TestCase::class) ->in('Feature') ->in('../resources/views/**');
This tells Pest to use your TestCase base class for tests found in both the tests/Feature directory and anywhere within resources/views.
Next, update your phpunit.xml file to include a test suite for component tests:
<testsuite name="Components"> <directory suffix=".test.php">resources/views</directory></testsuite>
Now Pest will recognize and run tests located next to your components when you run ./vendor/bin/pest.
Creating your first test
You can generate a test file alongside a component by appending the --test flag to the make:livewire command:
php artisan make:livewire post.create --test
For multi-file components, this will create a test file at resources/views/components/post/create.test.php:
<?php use Livewire\Livewire; it('renders successfully', function () { Livewire::test('post.create') ->assertStatus(200);});
For class-based components, this creates a PHPUnit test file at tests/Feature/Livewire/Post/CreateTest.php. You can convert it to Pest syntax or keep using PHPUnit—both work great with Livewire.
Testing a page contains a component
The simplest Livewire test you can write is asserting that a given endpoint includes and successfully renders a Livewire component.
it('component exists on the page', function () { $this->get('/posts/create') ->assertSeeLivewire('post.create');});
Tests like these are called "smoke tests"—they ensure there are no catastrophic problems in your application. Although simple, these tests provide enormous value as they require very little maintenance and give you a base level of confidence that your pages render successfully.
Browser testing
Pest v4 includes first-party browser testing support powered by Playwright. This allows you to test your Livewire components in a real browser, interacting with them just like a user would.
Installing browser testing
First, install the Pest browser plugin:
composer require pestphp/pest-plugin-browser --dev
Next, install Playwright via npm:
npm install playwright@latestnpx playwright install
For complete browser testing documentation, see the Pest browser testing guide.
Writing browser tests
Instead of using Livewire::test(), you can use Livewire::visit() to test your component in a real browser:
it('can create a new post', function () { Livewire::visit('post.create') ->type('[wire\:model="title"]', 'My first post') ->type('[wire\:model="content"]', 'This is the content') ->press('Save') ->assertSee('Post created successfully');});
Browser tests are slower than unit tests but provide end-to-end confidence that your components work as expected in a real browser environment.
For a complete list of available browser testing assertions, see the Pest browser testing assertions.
Use browser tests for critical user flows and complex interactions. For most component testing, the standard Livewire::test() approach is faster and sufficient.
Testing views
Livewire provides assertSee() to verify that text appears in your component's rendered output:
use App\Models\Post; it('displays posts', function () { Post::factory()->create(['title' => 'My first post']); Post::factory()->create(['title' => 'My second post']); Livewire::test('show-posts') ->assertSee('My first post') ->assertSee('My second post');});
Asserting view data
Sometimes it's helpful to test the data being passed into the view rather than the rendered output:
use App\Models\Post; it('passes all posts to the view', function () { Post::factory()->count(3)->create(); Livewire::test('show-posts') ->assertViewHas('posts', function ($posts) { return count($posts) === 3; });});
For simple assertions, you can pass the expected value directly:
Livewire::test('show-posts') ->assertViewHas('postCount', 3);
Testing with authentication
Most applications require users to log in. Rather than manually authenticating at the beginning of each test, use the actingAs() method:
use App\Models\User;use App\Models\Post; it('user only sees their own posts', function () { $user = User::factory() ->has(Post::factory()->count(3)) ->create(); $stranger = User::factory() ->has(Post::factory()->count(2)) ->create(); Livewire::actingAs($user) ->test('show-posts') ->assertViewHas('posts', function ($posts) { return count($posts) === 3; });});
Testing properties
Livewire provides utilities for setting and asserting component properties.
Use set() to update properties and assertSet() to verify their values:
it('can set the title property', function () { Livewire::test('post.create') ->set('title', 'My amazing post') ->assertSet('title', 'My amazing post');});
Initializing properties
Components often receive data from parent components or route parameters. Pass this data as the second parameter to Livewire::test():
use App\Models\Post; it('title field is populated when editing', function () { $post = Post::factory()->create([ 'title' => 'Existing post title', ]); Livewire::test('post.edit', ['post' => $post]) ->assertSet('title', 'Existing post title');});
Setting URL parameters
If your component uses Livewire's URL feature to track state in query strings, use withQueryParams() to simulate URL parameters:
use App\Models\Post; it('can search posts via url query string', function () { Post::factory()->create(['title' => 'Laravel testing']); Post::factory()->create(['title' => 'Vue components']); Livewire::withQueryParams(['search' => 'Laravel']) ->test('search-posts') ->assertSee('Laravel testing') ->assertDontSee('Vue components');});
Setting cookies
Use withCookie() or withCookies() to set cookies for your tests:
it('loads discount token from cookie', function () { Livewire::withCookies(['discountToken' => 'SUMMER2024']) ->test('cart') ->assertSet('discountToken', 'SUMMER2024');});
Calling actions
Use the call() method to trigger component actions in your tests:
use App\Models\Post; it('can create a post', function () { expect(Post::count())->toBe(0); Livewire::test('post.create') ->set('title', 'My new post') ->set('content', 'Post content here') ->call('save'); expect(Post::count())->toBe(1);});
The examples above use Pest's expect() syntax for assertions. For a complete list of available expectations, see the Pest expectations documentation.
You can pass parameters to actions:
Livewire::test('post.show') ->call('deletePost', $postId);
Testing validation
Assert that validation errors have been thrown using assertHasErrors():
it('title field is required', function () { Livewire::test('post.create') ->set('title', '') ->call('save') ->assertHasErrors('title');});
Test specific validation rules:
it('title must be at least 3 characters', function () { Livewire::test('post.create') ->set('title', 'ab') ->call('save') ->assertHasErrors(['title' => ['min:3']]);});
Testing authorization
Ensure authorization checks work correctly using assertUnauthorized() and assertForbidden():
use App\Models\User;use App\Models\Post; it('cannot update another users post', function () { $user = User::factory()->create(); $stranger = User::factory()->create(); $post = Post::factory()->for($stranger)->create(); Livewire::actingAs($user) ->test('post.edit', ['post' => $post]) ->set('title', 'Hacked!') ->call('save') ->assertForbidden();});
Testing redirects
Assert that an action performed a redirect:
it('redirects to posts index after creating', function () { Livewire::test('post.create') ->set('title', 'New post') ->set('content', 'Content here') ->call('save') ->assertRedirect('/posts');});
You can also assert redirects to named routes or page components:
->assertRedirect(route('posts.index'));->assertRedirectToRoute('posts.index');
Testing events
Assert that events were dispatched from your component:
it('dispatches event when post is created', function () { Livewire::test('post.create') ->set('title', 'New post') ->call('save') ->assertDispatched('post-created');});
Test event communication between components:
it('updates post count when event is dispatched', function () { $badge = Livewire::test('post-count-badge') ->assertSee('0'); Livewire::test('post.create') ->set('title', 'New post') ->call('save') ->assertDispatched('post-created'); $badge->dispatch('post-created') ->assertSee('1');});
Assert events were dispatched with specific parameters:
it('dispatches notification when deleting post', function () { Livewire::test('post.show') ->call('delete', postId: 3) ->assertDispatched('notify', message: 'Post deleted');});
For complex assertions, use a closure:
it('dispatches event with correct data', function () { Livewire::test('post.show') ->call('delete', postId: 3) ->assertDispatched('notify', function ($event, $params) { return ($params['message'] ?? '') === 'Post deleted'; });});
Using PHPUnit
While Pest is recommended, you can absolutely use PHPUnit to test Livewire components. All the same testing utilities work with PHPUnit's syntax.
Here's a PHPUnit example for comparison:
<?php namespace Tests\Feature\Livewire; use Livewire\Livewire;use App\Models\Post;use Tests\TestCase; class CreatePostTest extends TestCase{ public function test_can_create_post() { $this->assertEquals(0, Post::count()); Livewire::test('post.create') ->set('title', 'My new post') ->set('content', 'Post content') ->call('save'); $this->assertEquals(1, Post::count()); } public function test_title_is_required() { Livewire::test('post.create') ->set('title', '') ->call('save') ->assertHasErrors('title'); }}
All features documented on this page work identically with PHPUnit—just use PHPUnit's assertion syntax instead of Pest's.
If you're interested in exploring Pest's more elegant syntax and features, check out pestphp.com to learn more.
All available testing methods
Below is a comprehensive reference of every Livewire testing method available to you:
Setup methods
| Method | Description |
|---|---|
Livewire::test('post.create') |
Test the post.create component |
Livewire::test(UpdatePost::class, ['post' => $post]) |
Test the UpdatePost component with parameters passed to mount() |
Livewire::actingAs($user) |
Set the authenticated user for the test |
Livewire::withQueryParams(['search' => '...']) |
Set URL query parameters (ex. ?search=...) |
Livewire::withCookie('name', 'value') |
Set a cookie for the test |
Livewire::withCookies(['color' => 'blue', 'name' => 'Taylor']) |
Set multiple cookies |
Livewire::withHeaders(['X-Header' => 'value']) |
Set custom headers |
Livewire::withoutLazyLoading() |
Disable lazy loading for all components in this test |
Interacting with components
| Method | Description |
|---|---|
set('title', '...') |
Set the title property to the provided value |
set(['title' => '...', 'content' => '...']) |
Set multiple properties using an array |
toggle('sortAsc') |
Toggle a boolean property between true and false |
call('save') |
Call the save action/method |
call('remove', $postId) |
Call a method with parameters |
refresh() |
Trigger a component re-render |
dispatch('post-created') |
Dispatch an event from the component |
dispatch('post-created', postId: $post->id) |
Dispatch an event with parameters |
Assertions
| Method | Description |
|---|---|
assertSet('title', '...') |
Assert that a property equals the provided value |
assertNotSet('title', '...') |
Assert that a property does not equal the provided value |
assertCount('posts', 3) |
Assert that a property contains 3 items |
assertSee('...') |
Assert that the rendered HTML contains the provided text |
assertDontSee('...') |
Assert that the rendered HTML does not contain the provided text |
assertSeeHtml('<div>...</div>') |
Assert that raw HTML is present in the rendered output |
assertDontSeeHtml('<div>...</div>') |
Assert that raw HTML is not present in the rendered output |
assertSeeInOrder(['first', 'second']) |
Assert that strings appear in order in the rendered output |
assertDispatched('post-created') |
Assert that an event was dispatched |
assertNotDispatched('post-created') |
Assert that an event was not dispatched |
assertHasErrors('title') |
Assert that validation failed for a property |
assertHasErrors(['title' => ['required', 'min:6']]) |
Assert that specific validation rules failed |
assertHasNoErrors('title') |
Assert that there are no validation errors for a property |
assertRedirect() |
Assert that a redirect was triggered |
assertRedirect('/posts') |
Assert a redirect to a specific URL |
assertRedirectToRoute('posts.index') |
Assert a redirect to a named route |
assertNoRedirect() |
Assert that no redirect was triggered |
assertViewHas('posts') |
Assert that data was passed to the view |
assertViewHas('postCount', 3) |
Assert that view data has a specific value |
assertViewHas('posts', function ($posts) { ... }) |
Assert view data passes custom validation |
assertViewIs('livewire.show-posts') |
Assert that a specific view was rendered |
assertFileDownloaded() |
Assert that a file download was triggered |
assertFileDownloaded($filename) |
Assert that a specific file was downloaded |
assertUnauthorized() |
Assert that an authorization exception was thrown (401) |
assertForbidden() |
Assert that access was forbidden (403) |
assertStatus(500) |
Assert that a specific status code was returned |
See also
- Actions — Test component actions and interactions
- Forms — Test form submissions and validation
- Events — Test event dispatching and listening
- Components — Create testable component structure