We use cookies on this site to enhance your user experience

By clicking the Accept button, you agree to us doing so. More info on our cookie policy

Using Pest to test Laravel Livewire validation rules

Published: Aug 12, 2022 by C.S. Rhymes

Last year I wrote a post about testing Laravel Livewire validation rules with PHP Unit. This post uses the same techniques as that post, but shows how to transfer it to Pest instead of PHP Unit.

What is Pest?

If you haven’t heard of Pest before then here is a description from the Pest website.

Pest is a Testing Framework with a focus on simplicity. It was carefully crafted to bring the joy of testing to PHP.

Rather than writing standard PHP classes, it allows you to write tests in a more readable way, using test() or it() to describe the test in a more fluent way. If you have written tests in JavaScript before then this may seem very familiar to you.

Using Pest with Laravel and Livewire

Pest is a PHP testing framework built on top of PHP Unit and is not framework specific. Instead there are a range of plugins that allow you to use it with different tools and frameworks.

To get started with Laravel we can run the below commands to install Pest and the Pest plugin for Laravel, then use artisan to set up our Laravel project to start using Pest.

composer require pestphp/pest --dev --with-all-dependencies
composer require pestphp/pest-plugin-laravel --dev
php artisan pest:install

For Livewire, we need to install the Pest plugin for Livewire.

composer require pestphp/pest-plugin-livewire --dev

We can then run ./vendor/bin/pest or php artisan test to run our tests.

Converting the test

Below is the test from the original article (Testing validation rules in a Livewire component).

<?php

namespace Tests\Feature;

use App\Http\Livewire\ProfileForm;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class ProfileValidationTest extends TestCase
{
    use RefreshDatabase;

    /**
    * @test
    * @dataProvider validationRules
    **/
    public function test_validation_rules($field, $value, $rule)
    {
        $user = User::factory()->create();
        $anotherUser = User::factory()->create(['email' => 'duplicate@email.com'])

        Livewire::actingAs($user)
            ->test(ProfileForm::class, ['user' => $user])
            ->set($field, $value)
            ->call('save')
            ->assertHasErrors([$field => $rule]);
    }

    public function validationRules()
    {
        return [
            'name is null' => ['user.name', null, 'required'],
            'name is too long' => ['user.name', str_repeat('*', 201), 'max'],
            'email is null' => ['user.email', null, 'required'],
            'email is invalid' => ['user.email', 'this is not an email', 'email'],
            'email is not unique' => ['user.email', 'duplicate@email.com', 'unique'],
            'bio is null' => ['user.bio', null, 'required'],
            'bio is too short' => ['user.bio', str_repeat('*', 8), 'min'],
            'bio is too long' => ['user.bio', str_repeat('*', 1001), 'max'],
        ];
    }
}

To summarise, we create a test that accepts the field to test, the value and the validation rule that we expect to fail. This data is then passed into the test using a data provider, reusing the same test for the multiple datasets.

Writing a Pest test

The first thing that stands out is that you don’t need to create a PHP class, just a PHP file that ends in Test.php

As we need a user for the email we need to use the RefreshDatabase trait.

<?php

use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

Next we need to use the Livewire pest function to test our Livewire component, so let’s include that in our use statements, as well as the ProfileForm Livewire component we want to test.

<?php

use App\Http\Livewire\ProfileForm;
use Illuminate\Foundation\Testing\RefreshDatabase;
use function Pest\Livewire\livewire;

uses(RefreshDatabase::class);

Now we can start writing our test. We want to test the validation rules on the profile form component.

Rather than using the Livewire facade, we can use the livewire() function from the Pest Livewire plugin. We pass in the Livewire component class we want to test, then we can chain on other methods, such as set, call, assertSet, etc. as we would normally.

<?php

// use ...

it('tests the ProfileForm validation rules', function () {
    livewire(ProfileForm::class)
        ->call('save')
        ->assertHasErrors();
})

Rather than using a data provider, Pest has a with() method to chain on the dataset for your test. This is called an inline dataset.

You can also create reusable, shared datasets using dataset('my-dataset', []) and then call it in the with('my-dataset') after the it().

The dataset is an array, but as per the PHP Unit data provider, it’s beneficial to set a description for each dataset item so you can easily understand which dataset passes and which may fail.

Now we can put it all together and create the finished test.

<?php

use App\Http\Livewire\ProfileForm;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use function Pest\Livewire\livewire;

uses(RefreshDatabase::class);

it('tests the ProfileForm validation rules', function (string $field, mixed $value, string $rule) {
    $user = User::factory()->create();
    $anotherUser = User::factory()->create(['email' => 'duplicate@email.com']);

    $this->actingAs($user);

    livewire(ProfileForm::class, ['user' => $user])
        ->set($field, $value)
        ->call('save')
        ->assertHasErrors([$field => $rule]);
})->with([
    'name is null' => ['user.name', null, 'required'],
    'name is too long' => ['user.name', str_repeat('*', 201), 'max'],
    'email is null' => ['user.email', null, 'required'],
    'email is invalid' => ['user.email', 'this is not an email', 'email'],
    'email is not unique' => ['user.email', 'duplicate@email.com', 'unique'],
    'bio is null' => ['user.bio', null, 'required'],
    'bio is too short' => ['user.bio', str_repeat('*', 8), 'min'],
    'bio is too long' => ['user.bio', str_repeat('*', 1001), 'max'],
]);

Other Pest features

Have a read through the Pest docs to see the full range of features available. I’m still getting started with using it myself, but one of the really nice features I have found is the ability to run beforeEach() so you can do your set up in one method and it will be run before each test in the file.

An example of this is creating an admin user for all of the tests that need an admin user. Each test in the file will have access to $this->adminUser.

<?php

use App\Models\User;
use function Pest\Laravel\get;

beforeEach(function () {
    $this->adminUser = User::factory()
        ->create(['is_admin' => true]);
});

it('allows an admin to view manage users page', function () {
    actingAs($this->adminUser)
        ->get('/manage-users')
        ->assertOk();
});
Testing Livewire Pest

Latest Posts

Marketing an ebook with the 4Ps
Marketing an ebook with the 4Ps

I have seen a lot of threads from indie authors asking for advice with marketing their ebook. If I’m honest, I have never really had a strategy for marketing my books, I just assumed that I could put it out there and people would find it. In the real world, that does not seem to be the case.

Building a landing page for your book
Building a landing page for your book

This post follows on from my last post about using your website to promote your ebooks. The first step of the article explains that you need to make a website, but didn’t go into too much detail. This post aims at explaining how you can build a landing page for your book with Bulma Clean Theme.

How to promote your ebooks with your website?
How to promote your ebooks with your website?

I’m a web developer by trade and a part-time author, so here are a few things that I have done to help promote my books and ebooks using my website and my tech know how from my day job.

How NOT to make a website

How NOT to make a Website

By C.S. Rhymes

From £5.49 or read for free on Kindle Unlimited!

Nigel's Intranet Adventure

Nigel's Intranet Adventure

By C.S. Rhymes

From £4.99 or read for free on Kindle Unlimited!