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.
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'),
];