Skip to main content

Using Pest

Pest is a PHP Testing Framework. This document describes how to get started and provides some examples.

Getting started

Installation

To install Pest:

composer require pestphp/pest --dev --with-all-dependencies

To enable Laravel artisan commands, you should also install:

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

See more info here: https://pestphp.com/docs/plugins#content-laravel

Initializing

Pest will generate some files with the command below:

./vendor/bin/pest --init

There might already exist some test files in your Qore application, to make sure to start clean from scratch, delete the following files and then run the command above. Just make sure you don't delete CreatesApplication.php.

  • phpunit.xml
  • tests/Browser
  • tests/TestCase.php
  • tests/Unit
  • tests/Feature
  • tests/DuskTestCase.php

Running tests

To run Pest tests:

./vendor/bin/pest

This should pass 2 tests by default, 1 Unit test and 1 Feature test.

Setup for Qore

Because Qore uses multi-tenancy, there are a few steps we should take to make testing easier. Below is an (opinionated) way to set up these things.

Initialize tenant every test

In tests/TestCase.php you can override the setUp method which runs globally on every Feature test:

protected function setUp(): void
{
parent::setUp();

tenancy()->initialize(1);
boot_extensions();
}

Logging in user

In tests/Pest.php you can add helper functions which are available in every test. For example:

use App\Models\Central\User;
use function Pest\Laravel\actingAs;

function login(): void
{
$user = User::first();
actingAs($user);
}

Enabling / Disabling extensions

If you would like to enable/disable extensions before or after every test, you can do this in tests/Pest.php:

uses(Tests\TestCase::class)
->beforeAll(function () {
$app = require __DIR__.'/../bootstrap/app.php';
$app->make(Kernel::class)->bootstrap();

disableExtension('qore/brandfetch');
})
->afterAll(function () {
enableExtension('qore/brandfetch');
})
->in('Feature');

// Helper functions:
function enableExtension(string $name)
{
tenancy()->runForMultiple(Tenant::all(), function () use ($name) {
run('enable.extension', extension($name));
});
}

function disableExtension(string $name)
{
tenancy()->runForMultiple(Tenant::all(), function () use ($name) {
run('disable.extension', extension($name));
});
}

Writing tests

In the examples below, we will write some general tests to get started.

info

Do note that every test by default runs on your current database. If you would like to migrate:fresh on every test you can for example:

  • Use the trait that Pest/Laravel provides to refresh your database (potentially very slow)
  • Import an existing .sql file every test (fast but clumsy to manage, see example here: here)

Write a basic authentication test

Start by generating a new feature test:

php artisan pest:test AuthenticationTest

Next we can add a few basic tests:

use function Pest\Laravel\postJson;

it('returns a successful response when "/" is reached', function () {
$response = $this->get('/');
$response->assertStatus(200);
});

it('can not create a resource when a user is not authenticated', function () {
postJson('/api/resources/users', [])->assertStatus(401);
});

it('can login with the admin user', function () {
login();

expect(auth()->user())->toBeInstanceOf(User::class)
->and(auth()->id())->toBe(1);
});

Write a basic resource create test

We can add a test for creating a resource:

php artisan pest:test StoreTicketTest

Next to add a very basic create test:

use App\Models\Tenant\Ticket;
use function Pest\Laravel\postJson;

beforeEach(function () {
login();
});

it('can create a ticket', function () {
$data = ['name' => 'Test'];

$ticketsCount = Ticket::count();
postJson('/api/resources/tickets', $data)->assertStatus(200);
$this->assertCount($ticketsCount + 1, Ticket::select('id')->get());
});

Write a basic resource update test

Generate the test:

php artisan pest:test UpdateTicketTest

Then the test may look like this:

use App\Models\Tenant\Ticket;
use function Pest\Laravel\putJson;

beforeEach(function () {
login();
});

it('can update a ticket', function () {
$ticket = Ticket::first();
$newName = fake()->name;

$data = ['name' => $newName];

putJson("/api/resources/tickets/{$ticket->id}", $data)->assertStatus(200);

$this->assertTrue(Ticket::first()->name === $newName);
});

Using faker data

If you need random generated data, there is a useful Laravel plugin:

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

Then you can do:

use function Pest\Faker\fake;

$data = ['name' => fake()->name;];

// Or use locale:
fake('nl_NL')

See also: https://pestphp.com/docs/plugins#faker

Uploading files

You can upload "fake" files:

use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;

Storage::fake('tests');

$data = [
'name' => fake()->name,
'test_file' => UploadedFile::fake()->image('logo.png'),
];