In this guide, we’ll walk through how to create a unit test in a Laravel project using Pest, a modern testing framework built on top of PHPUnit.
If you’re familiar with PHPUnit, Pest provides a cleaner and more expressive syntax while still leveraging the same underlying functionality.
Before jumping into the steps, it’s helpful to understand what unit testing actually is.
A unit test is a small piece of code that checks whether another piece of your code behaves as expected. Think of it like this:
“If I give this function a certain input, does it return the correct output?”
For example, if you have a function that combines a user’s first and last name, a unit test would verify that given the arguments "John" and "Smith", the value "John Smith" would be returned.
Each test focuses on a single unit of functionality — usually one method or function — and confirms it works correctly.
Unit tests help you:
Instead of manually checking your app every time you make a change, you can run your tests and instantly know if something broke.
With our introduction to unit testing aside, let’s dig into Pest.
To install Pest, go to: https://pestphp.com/docs/installation
The first set of commands they give us in the instructions will install Pest in our Laravel project:
composer remove phpunit/phpunit
composer require pestphp/pest --dev --with-all-dependencies
During installation, you may see a prompt asking whether to remove PHPUnit. Since Pest uses PHPUnit under the hood, it’s safe to proceed.
Next, we’re told to initialize Pest using the following Artisan command:
php artisan pest:install
This command will create a Pest configuration file at tests/Pest.php. Open this file and chain on the command ->in('Unit') so that in addition to looking for tests in tests/Feature, the test runner will also locate nd run tests in testts/Unit:
pest()->extend(TestCase::class)
// ->use(Illuminate\Foundation\Testing\RefreshDatabase::class)
->in('Feature')
->in('Unit'); # ⭐ Add this line
For this example, we have a Customer Model with a simple method called getFullName that concatenates the customer’s first and last name. This is the test we will write our Unit test for.
<?php
# App\Models\Customer.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Customer extends Model
{
protected $fillable = [
'first_name',
'last_name',
'email'
];
public function getFullName(): string {
return $this->first_name . ' ' . $this->last_name;
}
}
To generate test for our Customer model, run the following command:
php artisan make:test CustomerTest --unit
This command creates a test file at tests/Unit/CustomerTest.php and sets it up specifically for unit testing.
Open tests/Unit/CustomerTest.php and replace the default example with a test for our customer model.
<?php
use App\Models\Customer;
test('customer getFullName returns properly formatted full name', function () {
# Setup
$customer = new Customer([
'first_name' => 'John',
'last_name' => 'Smith'
]);
$fullName = $customer->getFullName();
# Assertion (every test must include at least one assertion)
expect($fullName)->toEqual('Smith, John');
});
Observe that Pest uses expect() instead of traditional assertions; see https://pestphp.com/docs/expectations for a list of available expectation methods.
To run all tests:
php artisan test
You should see your test, in addition to some default/existing tests, all pass successfully. Example:
/Users/Susan/Herd/demo $ php artisan test
PASS Tests\Unit\CustomerTest
✓ customer getFullName returns properly formatted full name 0.06s
PASS Tests\Unit\ExampleTest
✓ that true is true 0.01s
PASS Tests\Feature\ExampleTest
✓ the application returns a successful response 0.03s
Tests: 3 passed (3 assertions)
Duration: 0.16s
If your project has many tests, you can run just one:
php artisan test --filter=CustomerTest
This is useful for faster iteration while developing.
Pest provides a clean, readable way to write tests in Laravel while still leveraging PHPUnit behind the scenes. By focusing on clear test names and simple assertions, you can quickly build confidence in your application’s behavior.
No subscriptions, no auto-renewals.
Just a simple one-time payment that helps support my free, to-the-point videos without sponsered ads.
Unlocking gets you access to the notes for this video plus all 200+ guides on this site.
Your support is appreciated. Thank you!