Custom Pest expectations for Inertia
I recently converted a Laravel project from PHPUnit to Pest[↗]. The project is built using Vue, Inertia and Laravel and so our tests contained a lot of assertInertia
assertions.
That code looks something like this:
use Inertia\Testing\AssertableInertia as Assert;
$response = $this->get('/');
$response->assertInertia(fn (Assert $page) => $page
->component('Some/View')
->where('data', [
'some' => 'value'
]));
This never really sat right with me. For a start there's a lot of standard code that was repeated throughout our codebase, including the ugly alias at the top of every file.
Looking a the Pest docs I saw it's possible to create your own custom expectations[↗]. I figured an API like this would be better:
$response = $this->get('/');
expect($response)
->toBeInertiaView('Some/View')
->where('data', [
'some' => 'value'
]);
The code is shorter, is more easily readable and the alias is gone.
After a lot of fiddling with the various internal workings of the Inertia assertion, I ended up with the following three custom expectations:
use Inertia\Testing\AssertableInertia as Assert;
// Assert response is an Inertia page template
expect()->extend('toBeInertiaView', function (string $view) {
// Prevent unhelpful 'Not an Inertia response' test fail message
$this->value->assertOk();
$this->value->assertInertia(fn (Assert $page) => $page->component($view));
});
// Assert Inertia response matches given array exactly
expect()->extend('where', function ($key, $data = null) {
if (is_array($key)) {
$this->value->assertInertia(fn (Assert $page) => $page->whereAll($key));
} else {
$this->value->assertInertia(fn (Assert $page) => $page->where($key, $data));
}
});
// Assert Inertia response contains array data
expect()->extend('containing', function (string $key, array $data) {
$this->value->assertInertia(fn (Assert $page) => $page->has($key, function ($page) use ($data) {
foreach ($data as $key => $value) {
$page->where($key, $value);
}
$page->etc();
}));
}
The first expectation just tests that the Inertia view is correct (and prints a stack trace for any unexpected exceptions with the assertOk()
).
The second expectation mimics the original where()
method of the Assert class, but adds the option to test a whole array of data in one go instead of repeating the where multiple times.
The last expectation was the most difficult to get working but it allows checking that a response contains a specific key and that the provided value matches that key in the response.
Bonus
The project I was working on also makes use of a lot of Inertia lazy loading. During the refactor when implementing the above code I also noticed a lot of lazy load headers being use during testing:
$response = $this->withHeaders([
'X-Inertia-Partial-Component' => 'Some/View',
'X-Inertia-Partial-Data' => 'prop'
])->get('/');
So I made a quick public method in the base test class so I wouldn't have to keep writing out the header names each time. Now our lazy loading tests look like this:
$response = $this->withLazyLoadHeaders('Some/View', 'prop')->get('/');
A nice little readability improvement, if I do say so myself.
Looking at it now you could perhaps even combine the get()
into the withLazyLoadHeaders()
method, as I can't think of a scenario when you would want to lazy load data that isn't a GET request... Here's to the next refactor.