Advanced Pest setup

September 12, 2021
English

Pest PHP is a relatively new PHP testing framework. It has a nice syntax, and a few cool features built on top of PHPunit. I already wrote an article on how to convert a PHPunit test suite to Pest, but in this article I want to take a look at some cool Pest techniques and my Pest configuration.

The TestCase class

First of all, I want to start at the TestCase class. I haven't done something special with it, but I would like to show it anyways.

<?php

// tests/Pest.php

uses(Tests\TestCase::class)->in('Feature', 'Unit');

The RefreshDatabase trait

Because I use the RefreshDatabase in almost all of my tests, I like to configure it globally.

For tests that don't use the database, this will cost a little extra time. But it is neglectable and it doesn't outweight the advantage of not having to repeat it in every test.

<?php

// tests/Pest.php

uses(Tests\TestCase::class)->in('Feature', 'Unit');
uses(Illuminate\Foundation\Testing\RefreshDatabase::class)->in('Feature', 'Unit');

Because the trait autoruns the migrations, no further actions are required to migrate the database for every test.

The WithFaker trait

Faker is a handy library to get fake testing data, such as fake names, emails and photos.

Pest has its own plugin for faker, but it requires you to import the Pest\Faker\faker function each time you want to use Faker:

use function Pest\Faker\faker;

it('has a name', function () {
    $name = faker()->word();
    $project = Project::factory()->create(['name' => $name]);
    expect($project->name)->toBe($name);
});

Instead I'd like to add the WithFaker trait in the Pest configuration file:

<?php

// tests/Pest.php

uses(Tests\TestCase::class)->in('Feature', 'Unit');
uses(Illuminate\Foundation\Testing\RefreshDatabase::class)->in('Feature', 'Unit');
uses(Illuminate\Foundation\Testing\WithFaker::class)->in('Feature', 'Unit');

This way I can remove the import in the test:

use function Pest\Faker\faker;

it('has a name', function () {
    $name = faker()->word();
    $name = $this->faker->word();
    $project = Project::factory()->create(['name' => $name]);
    expect($project->name)->toBe($name);
});

The withoutExceptionHandling() method

When testing, it's common to add $this->withoutExceptionHandling() to your tests for better debugging. It will add more clear error messages.

Instead of adding it manually to every test, I like to add it globally for all my tests:

<?php

// tests/Pest.php

uses(Tests\TestCase::class)->in('Feature', 'Unit');
uses(Illuminate\Foundation\Testing\RefreshDatabase::class)->in('Feature', 'Unit');
uses(Illuminate\Foundation\Testing\WithFaker::class)->in('Feature', 'Unit');
uses()->beforeEach(fn () => $this->withoutExceptionHandling())->in('Feature', 'Unit');

This way it is enabled by default, and if I want I can disable it again in a test:

it('has a name', function () {
    $this->withExceptionHandling();
    $name = $this->faker->word();
    $board = Board::factory()->create(['name' => $name]);
    expect($board->name)->toBe($name);
});

Conclusion

Some things are annoying to repeat over and over, and fortunately you can add them globally in Pest. Thanks for reading!