Skip to content

Pest

Vue d'ensemble

Pest est un framework de test moderne pour PHP qui offre une syntaxe élégante et expressive, construit sur PHPUnit avec une approche orientée simplicité et lisibilité.

Avantages clés

  • Syntaxe expressive : Tests lisibles et maintenables
  • Zero configuration : Prêt à l'emploi avec Laravel
  • Plugins riches : Expectations, snapshots, parallel testing
  • Compatible PHPUnit : Migration facile

Syntaxe de base

<?php

// Test simple
it('can add two numbers', function () {
    expect(1 + 1)->toBe(2);
});

// Test avec describe
describe('Calculator', function () {
    it('can add numbers', function () {
        $result = add(2, 3);
        expect($result)->toBe(5);
    });

    it('can subtract numbers', function () {
        $result = subtract(5, 3);
        expect($result)->toBe(2);
    });
});

// Test avec beforeEach
describe('User management', function () {
    beforeEach(function () {
        $this->user = User::factory()->create();
    });

    it('can update user name', function () {
        $this->user->update(['name' => 'New Name']);
        expect($this->user->fresh()->name)->toBe('New Name');
    });
});

Testing Laravel avec Pest

<?php

use App\Models\User;
use App\Models\Post;

// Feature test
it('can register a new user', function () {
    $userData = [
        'name' => 'John Doe',
        'email' => 'john@example.com',
        'password' => 'password',
        'password_confirmation' => 'password',
    ];

    $response = $this->post('/register', $userData);

    $response->assertRedirect('/dashboard');
    $this->assertDatabaseHas('users', [
        'name' => 'John Doe',
        'email' => 'john@example.com',
    ]);
});

// API testing
it('can fetch users via API', function () {
    $users = User::factory()->count(3)->create();

    $response = $this->getJson('/api/users');

    $response->assertStatus(200)
            ->assertJsonCount(3, 'data')
            ->assertJsonStructure([
                'data' => [
                    '*' => ['id', 'name', 'email', 'created_at']
                ]
            ]);
});

// Database testing avec RefreshDatabase
uses(RefreshDatabase::class);

it('can create a post', function () {
    $user = User::factory()->create();

    $postData = [
        'title' => 'Test Post',
        'content' => 'This is a test post content.',
    ];

    $response = $this->actingAs($user)->post('/posts', $postData);

    $response->assertRedirect()
            ->assertSessionHas('success');

    $this->assertDatabaseHas('posts', [
        'title' => 'Test Post',
        'user_id' => $user->id,
    ]);
});

Expectations avancées

<?php

// Collections
it('can work with collections', function () {
    $users = collect([
        ['name' => 'John', 'age' => 25],
        ['name' => 'Jane', 'age' => 30],
    ]);

    expect($users)
        ->toHaveCount(2)
        ->first()->toMatchArray(['name' => 'John', 'age' => 25]);
});

// JSON expectations
it('returns correct API response', function () {
    $response = $this->getJson('/api/user/1');

    expect($response->json())
        ->toHaveKey('data')
        ->and($response->json('data'))
        ->toMatchArray([
            'id' => 1,
            'name' => 'John Doe'
        ]);
});

// Exception expectations
it('throws exception for invalid data', function () {
    expect(fn() => new User(['invalid' => 'data']))
        ->toThrow(ValidationException::class);
});

// File expectations
it('creates log file', function () {
    Log::info('Test message');

    expect(storage_path('logs/laravel.log'))
        ->toBeFile()
        ->and(file_get_contents(storage_path('logs/laravel.log')))
        ->toContain('Test message');
});

Datasets pour tests paramétrés

<?php

// Dataset simple
it('validates email formats', function ($email, $isValid) {
    $validator = Validator::make(['email' => $email], ['email' => 'email']);

    expect($validator->passes())->toBe($isValid);
})->with([
    ['valid@email.com', true],
    ['invalid-email', false],
    ['another@valid.co.uk', true],
    ['@invalid.com', false],
]);

// Dataset avec noms
it('calculates tax correctly', function ($amount, $rate, $expected) {
    expect(calculateTax($amount, $rate))->toBe($expected);
})->with([
    'standard rate' => [100, 0.2, 20],
    'reduced rate' => [100, 0.1, 10],
    'zero rate' => [100, 0, 0],
]);

// Dataset depuis provider
function priceProvider()
{
    return [
        [10.99, '€10.99'],
        [0, '€0.00'],
        [1234.56, '€1,234.56'],
    ];
}

it('formats prices correctly', function ($price, $expected) {
    expect(formatPrice($price))->toBe($expected);
})->with('priceProvider');

Mocking et Fakes

<?php

use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Queue;
use Illuminate\Support\Facades\Mail;

// HTTP Mock
it('can handle external API', function () {
    Http::fake([
        'api.example.com/*' => Http::response(['status' => 'success'], 200)
    ]);

    $response = Http::get('https://api.example.com/data');

    expect($response->json())->toHaveKey('status', 'success');

    Http::assertSent(function ($request) {
        return $request->url() === 'https://api.example.com/data';
    });
});

// Queue testing
it('dispatches job', function () {
    Queue::fake();

    dispatch(new ProcessUser($user));

    Queue::assertPushed(ProcessUser::class, function ($job) use ($user) {
        return $job->user->id === $user->id;
    });
});

// Mail testing
it('sends welcome email', function () {
    Mail::fake();

    $user = User::factory()->create();

    Mail::assertSent(WelcomeEmail::class, function ($mail) use ($user) {
        return $mail->hasTo($user->email);
    });
});

Custom Expectations

<?php

// Expectation personnalisée
expect()->extend('toBeValidEmail', function () {
    return $this->toMatch('/^[^\s@]+@[^\s@]+\.[^\s@]+$/');
});

// Utilisation
it('validates email format', function () {
    expect('user@example.com')->toBeValidEmail();
    expect('invalid-email')->not->toBeValidEmail();
});

// Expectation complexe pour Laravel
expect()->extend('toHaveValidationError', function ($field) {
    $errors = $this->value->errors();

    expect($errors->has($field))->toBeTrue();

    return $this;
});

// Utilisation
it('validates required fields', function () {
    $response = $this->postJson('/api/users', []);

    expect($response)->toHaveValidationError('name')
                    ->toHaveValidationError('email');
});

Configuration et Hooks

<?php

// tests/Pest.php
uses(Tests\TestCase::class)->in('Feature');
uses(RefreshDatabase::class)->in('Feature/Api');

// Global beforeEach
beforeEach(function () {
    // Configuration globale
})->in('Feature');

// Custom test trait
trait CreatesUsers
{
    protected function createUser($attributes = [])
    {
        return User::factory()->create($attributes);
    }
}

uses(CreatesUsers::class)->in('Feature/Users');

Ressources