How to test Laravel Livewire using PHPUnit

Sebastian Ravenscroft
6 min readMay 19, 2021

In this guide we will look at how we can test Laravel Livewire components.

We’ll start with a clean install of Laravel 8 and pull in Livewire 2 via the terminal:

composer require livewire/livewire

Laravel comes with the PHPUnit testing suite installed and you can run your tests from the terminal:

./vendor/bin/phpunit

Our first test

Let’s say we want to build a page where the user can edit their profile. We can use Laravel’s Artisan command line to generate our first test class:

php artisan make:test EditProfileTest

That created a new file called /tests/Feature/EditProfileTest.php. Open it up and replace the example test with the following:

We are simulating a user logging into the app and visiting our /profile/edit route. We simply want to check that the page exists and we get a 200 response.

Before we run the test we need to set up a couple of things. First update the parent tests/TestCase.php class to pull in the RefreshDatabase trait:

When we use the RefreshDatabase trait, PHPUnit takes care of migrating our test database, and resetting it between each test.

We also need to update our phpunit.xml config file in the root directory in order to choose which database to use for testing. Simply uncomment the two lines at the bottom that specify the DB_CONNECTION and DB_DATABASE environment variables:

For this demo we’re going for an SQLite database running in memory — for commercial projects you probably want to test on the same kind of database you are running in production to discover any issues that could be specific to that database.

And that’s it we’re ready to run our test, which we expect to fail:

./vendor/bin/phpunit

As expected our app returned a 404 because the page wasn’t found — we haven’t created it yet.

In test driven development we aim for a short test cycle. We write a small test which we know will fail, and then write just enough application code to make that test pass.

So to make this test pass we need to register the /profile/edit route in the /routes/web.php file:

And that’s enough to make our test pass!

At any point you can also check the progress manually. Use Artisan to serve up the app and try visiting the /profile/edit route in your web browser.

php artisan serve

Sure enough you’ll see an empty web page.

Test that a Livewire component is rendered

Add a new test to the end of the EditProfileTest.php file:

This tests for the presence of a Livewire component called ‘edit-profile’. Run PHPUnit and you’ll see the test fails with this error:

Cannot find Livewire component [edit-profile] rendered on page.

To make the test pass start by using Artisan to create a new Livewire component:

php artisan livewire:make EditProfile

This created two new files for us:

  • app/Http/Livewire/EditProfile.php
  • resources/views/livewire/edit-profile.blade.php

Now update the routes/web.php file so the profiles/edit route points directly at our new EditProfile component:

Finally we need to create a layout file for our Livewire components. Create a simple HTML layout file and save it to resources/views/layouts/app.blade.php

Notice the {{ $slot }} line in the body which is a Blade ‘slot’. This is where our Livewire pages will be rendered.

And that does it, our tests are back to green!

Testing Laravel Models and Factories

We want to test that we can use our form to edit the user’s profile. But first we need to be able to make profiles and assign them to users.

Make a new file called ProfilesTest.php:

php artisan make:test ProfilesTest

Once again write the simplest test we can which will fail:

./vendor/bin/phpunit

And as expected it fails because we don’t have a Profile class. Create the Profile model and import it into the ProfilesTest.php file.

php artisan make:model Profile

If you run the tests again they fail but for a different reason:

Each time we can use the hint from the test suite to help us decide what to do next. Now we’re missing the ProfileFactory which we can create via Artisan:

php artisan make:factory Profile

Running our tests again we get a new error — this time the table is missing.

We can easily create a database migration using Artisan:

php artisan make:migration php artisan make:factory Profile

Before we actually write the migration let’s try running our tests again.

This time they don’t fail! We get a warning because our test isn’t actually making any assertions but the test suite passes.

Following the TDD cycle we can extend our test to make it fail again:

Notice we called a forUser() method on the Profile factory. That method doesn’t exist yet.

First we think about the API we would like to have, and then we’ll find a way to build it. This ‘wishful thinking’ approach can help you design very useable APIs.

Sure enough it fails and now we can start writing our code. Add a forUser() method to the ProfileFactory, making use of Laravel factory states.

If you run your tests now they fail for a different reason:

We’re trying to save a row to the profiles table but it doesn’t have a user_id column. Update the CreateProfilesTable.php migration file to create the column.

We added an index to the user_id column. When we are likely to search for records by user the user_id column is usually a good candidate for an index.

Try the tests again and we’re back to green!

Next let’s extend that test to expect that a profile can belong to a user.

We haven’t defined the relationship yet so the test will fail:

Add the user() relationship to the Profile.php model to make the test pass:

With the new relation our test passes:

Refactor the test, and add another test to check that a User can have a related Profile:

The new test fails because we don’t have a profile() relation on the User model:

Add a HasOne relationship to the User model:

Now if you check your tests they will all pass again:

Finally before we get back to the Livewire work let’s add one more test asserting that we can set an ‘bio’ field on the Profile model.

As always check our assumption that the test will fail:

We are missing the withBio() method on the ProfileFactory so create it now.

Now we are failing because we are missing the ‘bio’ field on our table, so we can add it to the migration:

Run the tests and you’ll see they all pass again — this is solid progress.

Testing Livewire Forms

Now we have built a basic Profile model we can go ahead with building the Edit Profile form.

--

--