← Tous les articles
Sam. 18 avril 2026 · 4 min de lecture

🐛 Pest 4 : du browser testing natif, et plus aucune excuse pour ne pas tester

Pest 4

📚 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/pest

Le --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 install

Un 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-drift pour 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 --mutate une 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-playwright dans actions/cache pour gagner ~2 min par run.
  • Tests browser flaky : comme partout, les tests browser introduisent du non-dĂ©terminisme. Utilise $page->waitForText() plutĂŽt que des sleep() magiques.
  • Migration PHPUnit → Pest : pest-plugin-drift couvre 90 % des cas mais relis les conversions. Les setUp() deviennent des hooks beforeEach().
  • 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