How to use Pest PHP with Laravel – Full Guide (2023)

Ralph J. Smit Laravel Software Engineer

Testing is a crucial skill for every developer. When building applications it might initially feel like some wasted time, but I can say from experience that it gives so much peace of mind later on. In this article I'll show you how to

Most people and frameworks still use PHPUnit, which is an awesome testing framework. Currently we're also seeing a more broader shift to using Pest. Pest provides a nicer and more fluent way of testing, so it's certainly a good option. In this article I'll show you the basics of Pest, so that you can get started with it quickly.

Learning Pest is not difficult. The syntax is a little bit different from PHPUnit, but how you test is mostly the same.

Installation

The installation of Pest is very simple. Run the following commands to install and configure Pest:

composer require pestphp/pest --dev --with-all-dependencies
 
# For Laravel:
composer require pestphp/pest-plugin-laravel --dev
php artisan pest:install
 
# For other project types create a /tests folder
./vendor/bin/pest --init

Writing our first test

Now, we're ready to write our first test! Create a new folder tests/Unit and in the unit folder a HomePageTest.php file. Let's get started writing a test.

A test file looks like this:

<?php
 
it('can display the homepage', function () {
// Do testing logic here
});
 
// Instead of 'it', you can also use 'test'

Each test usually consists of three steps:

  1. Prepare your test first (creating models from factories, other setup)

  2. Act – do the test, execute the code that is being tested

  3. Assert that the 'Act' step has had the intended consequences

use App\Models\Post;
 
it('can display a post on the homepage', function () {
// Prepare
$post = Post::factory()->create();
 
// Act
$response = $this->get(route('home'));
 
// Assert
$response->assertOk();
 
});

As you can see, Pest has all the same methods available as on PHPUnit:

it('is a dummy test', function () {
$post = Post::factory()->create(['title' => 'Hello']);
 
$this->assertSame('Hello', $post->title);
});

There are many helpers available. Check out the documentation of PHPUnit to learn about all of the available assertions.

Expectation API

Now comes the interesting part, where Pest really stands apart from PHPUnit. In the above examples we used the $this->assertSame(...) methods to do an assertion. Pest also offers the so-called Expectation API, where you can write tests as if they were plain English. Let's refactor the previous example to use expectations:

it('is a dummy test', function () {
$post = Post::factory()->create(['title' => 'Hello']);
 
$title = $post->title;
 
expect($title)->toBe('Hello');
});

Great, right? This is way more understandable and fluent! But, you might ask, isn't it a bit cumbersome as well to store the $title in a separate variable? It is, and Pest also has a solution for that. That solution is called a Higher Order Expectation and it looks like this:

it('is a dummy test', function () {
$post = Post::factory()->create(['title' => 'Hello']);
 
expect($post)
->title
->toBe('Hello');
});

See, how nice? A Higher Order expectation basically means that you can call a property or function on the expect(...) helper, and Pest will make the assertion on that property or function. So in the above example, Pest will make an assertion on $post->title. After making the assertion, it will reset the value of expect(...) to the $post again.

You can also chain multiple Higher Order expectations:

it('is a dummy test', function () {
$post = Post::factory()->create(['title' => 'Hello']);
 
expect($post)
->title->toBe('Hello')
->body->toBeNull();
});

You can also negate the expectation by adding ->not before it:

it('is a dummy test', function () {
$post = Post::factory()->create(['title' => 'Hello']);
 
expect($post)
->title->not->toBeArray()
->body->toBeNull();
});

There are many, many expectations, like ->toBeArray(), ->toBeString(), toStartWith(), toThrow() and many others. Check out the Pest docs for expectations.

Writing custom Pest expectations

You can also write our own custom Pest expectation by using the expect()->extend(...) function. See the following example from the Pest docs:

expect()->extend('toBeWithinRange', function ($min, $max) {
return $this
->toBeGreaterThanOrEqual($min)
->toBeLessThanOrEqual($max);
});
 
test('numeric ranges', function () {
expect(100)->toBeWithinRange(90, 110);
});

Running something before or after each test

It is very common to execute certain code directly before or after running a test. This allows you to do some preparation or other interesting stuff. Pest has four helpers for that:

  1. beforeAll()

  2. afterAll()

  3. beforeEach()

  4. afterEach()

I think the names are pretty clear about what they do, but here's an example again from the docs with the order:

beforeAll(fn () => dump('beforeAll'));
afterAll(fn () => dump('afterAll'));
 
beforeEach(fn () => dump('beforeEach'));
afterEach(fn () => dump('afterEach'));
 
test('example 1', fn () => dump('test foo'));
test('example 2', fn () => dump('test bar'));
 
// "beforeAll"
// "beforeEach"
// "test foo"
// "afterEach"
// "beforeEach"
// "test bar"
// "afterEach"
// "afterAll"

Next steps & plugins

The above testing methods and techniques are really the basics of using Pest and there's so much more to learn. I'd advise you to take a good look at the Pest PHP documentation to make sure you're not missing out on great features. There are also many plugins, so take a look at them too.

Here's some topics you could get started with:

  1. Creating a Pest.php testcase

  2. Catching exceptions

  3. Using datasets

  4. Laravel plugin

Conclusion

In this article I introduced you to the basics of testing with Pest. I hope you enjoyed learning about it!

As always, let me know if you have any questions or comments.

Published by Ralph J. Smit on in Testing . Last updated on 22 February 2023 .