🐛 Pest 4 : du browser testing natif, et plus aucune excuse pour ne pas tester
📚 Introduction
J’écris du PHPUnit depuis qu’il existe, et j’ai mis du temps à passer à Pest. Pas par dogme, plutôt par inertie : PHPUnit fait le job, mes vieux tests tournent, pourquoi changer. La version 4 me fait changer d’avis. Le browser testing intégré (basé sur Playwright) tue d’un coup le combo Dusk + Cypress que je traînais comme un boulet sur mes projets Laravel.
Pest 4 demande PHP 8.3+ et reste compatible PHPUnit en dessous. Tu installes le binaire, tu importes ton existant via pest-plugin-drift, et tu commences à écrire les nouveaux tests dans la syntaxe Pest. Pas de big bang. Ce post couvre ce qui change concrètement entre PHPUnit et Pest 4, et pourquoi le browser plugin justifie à lui seul la bascule.
🚀 Install et syntaxe
L’install standard :
composer require pestphp/pest --dev --with-all-dependencies
./vendor/bin/pest --init
./vendor/bin/pestLe --init génère un Pest.php à la racine de tes tests. C’est ton point de configuration central pour les uses(), les hooks et les helpers globaux.
La syntaxe descriptive vient de Jest et RSpec :
it('crée un utilisateur quand le payload est valide', function () {
$response = $this->postJson('/users', [
'email' => 'arnaud@arkoder.dev',
'name' => 'Arnaud',
]);
expect($response)
->status()->toBe(201)
->and(User::count())->toBe(1);
});
describe('UserController', function () {
it('rejette un email invalide', function () {
$response = $this->postJson('/users', ['email' => 'pas-un-email']);
expect($response->status())->toBe(422);
});
it('rejette un email déjà pris', function () {
User::factory()->create(['email' => 'arnaud@arkoder.dev']);
expect($this->postJson('/users', ['email' => 'arnaud@arkoder.dev'])->status())->toBe(422);
});
});Lisible, sans extends TestCase. Le bénéfice s’accumule sur 200 tests. Les messages d’erreur sont aussi plus utiles que la stack PHPUnit standard.
🌐 Browser testing avec Playwright
C’est la feature majeure de Pest 4. Le plugin pest-plugin-browser lance Playwright derrière une API qui ressemble à la syntaxe Pest. Plus besoin de Dusk (et de son setup ChromeDriver à l’ancienne) ni d’une suite Cypress en parallèle.
composer require pestphp/pest-plugin-browser --dev
npm install playwright@latest
npx playwright installUn test de navigation type :
it('permet à un utilisateur de se connecter', function () {
User::factory()->create([
'email' => 'arnaud@arkoder.dev',
'password' => 'password',
]);
$page = visit('/')->on()->mobile()->firefox();
$page->click('Sign In')
->assertUrlIs('/login')
->fill('email', 'arnaud@arkoder.dev')
->fill('password', 'password')
->click('Submit')
->assertSee('Dashboard');
});Tu choisis le navigateur (chromium, firefox, webkit), tu peux émuler un device (iPhone14Pro, Macbook14, etc.), et tu te branches sur tes factories Laravel comme dans un test unitaire. Pour les tests qui valident un parcours critique (checkout, signup, dashboard), c’est exactement le bon niveau.
Combiné à un job GitHub Actions, tu obtiens du browser testing déterministe en CI sans monter de pipeline parallèle Cypress.
🧪 Architecture testing et snapshots
Deux modules très utiles :
Architecture testing : tu décris des règles structurelles sur ton code et Pest vérifie qu’elles sont respectées. Tu enforces une discipline sans relire toute la PR.
arch('Aucun debug en prod')
->expect(['dd', 'dump', 'ray', 'var_dump'])
->not->toBeUsed();
arch('Les contrôleurs n''appellent pas Eloquent directement')
->expect('App\Http\Controllers')
->not->toUse('Illuminate\Database\Eloquent\Builder');Snapshot testing : tu génères un snapshot d’une sortie (HTML, JSON, vue Blade compilée) et chaque exécution future compare. Détecte les régressions silencieuses sans écrire 30 assertions manuelles.
it('rend la page projets', function () {
$response = $this->get('/projets');
expect($response->content())->toMatchSnapshot();
});Quand le snapshot doit changer (refonte intentionnelle), tu mets à jour avec pest -d --update-snapshots.
⚡ Parallèle, mutations, coverage
Pest 4 livre nativement :
--parallel: tes tests tournent sur plusieurs processus. Sur une grosse suite (>500 tests), le gain est massif (3-5×). Pas de plugin externe.--mutate: mutation testing intégré. Pest modifie ton code (un opérateur, un return) et vérifie que tes tests cassent. Sinon : ton code est sous-testé. Très lent à exécuter, donc à lancer ponctuellement pour évaluer la qualité réelle des tests.--coverage --min=80: la couverture en ligne de commande, avec un seuil minimal. Idéal pour la CI.--profile: repère les 10 tests les plus lents. Utile pour optimiser ce qui mérite vraiment d’être optimisé.
🎯 Quand basculer
- Tu démarres un nouveau projet Laravel ou Symfony : prends Pest direct.
- Tu maintiens une suite PHPUnit existante : installe Pest à côté, utilise
pest-plugin-driftpour convertir progressivement. - Tu veux lâcher Dusk / Cypress : le plugin browser fait le job sans pipeline parallèle.
- Tu veux mesurer la qualité réelle de tes tests : lance
--mutateune fois par sprint.
⚠️ Quelques précautions
- Playwright en CI : la première exécution télécharge les binaires (Chromium ≈ 200 Mo). Cache le dossier
~/.cache/ms-playwrightdansactions/cachepour gagner ~2 min par run. - Tests browser flaky : comme partout, les tests browser introduisent du non-déterminisme. Utilise
$page->waitForText()plutôt que dessleep()magiques. - Migration PHPUnit → Pest :
pest-plugin-driftcouvre 90 % des cas mais relis les conversions. LessetUp()deviennent des hooksbeforeEach(). - Mutation testing : très utile en audit, déraisonnable en CI nominal. Lance-le manuellement.
🎉 Conclusion
Pest 4 fait passer le DX du testing PHP au niveau Jest / Vitest. La syntaxe descriptive est plus agréable, le browser testing élimine une grosse partie du setup Dusk, et les outils annexes (arch, mutation, parallèle) couvrent ce que tu installais en plugins séparés.
Si tu hésitais encore, c’est probablement le moment. La courbe d’apprentissage est courte si tu connais PHPUnit, et la migration peut se faire test par test sans bloquer le release cycle. Combine avec Pail pour le debug et Boost pour les agents IA et tu obtiens une boucle de feedback locale très solide.
🔗 Liens utiles
/faq
Questions fréquentes
Quelle version de PHP Pest 4 exige-t-il ?
+
Pest 4 demande PHP 8.3 minimum et reste compatible PHPUnit en dessous.
Sur quoi repose le browser testing de Pest 4 ?
+
Le plugin pest-plugin-browser s'appuie sur Playwright, ce qui permet de tester son interface sans Dusk ni Cypress. On peut choisir le navigateur (chromium, firefox, webkit) et émuler un device.
Comment migrer une suite PHPUnit existante vers Pest 4 ?
+
On installe Pest à côté de l'existant et on utilise pest-plugin-drift pour convertir progressivement, test par test. Le plugin couvre environ 90 % des cas, et les setUp() deviennent des hooks beforeEach().