# Paralléliser une suite PHPUnit sur plusieurs cœurs pour gagner du temps

> Une suite de tests qui dure de plus en plus longtemps ralentit toute l'équipe. Voici comment répartir son exécution sur plusieurs cœurs sans perdre en couverture.

- Auteur : WordPress Développement
- Publié le : 2023-10-23
- Mis à jour le : 2023-10-23
- Catégorie : Tests
- URL : https://www.wpmoderne.fr/tests/paralleliser-suite-phpunit-plusieurs-coeurs/

## L’essentiel

- paratest répartit les classes de test sur plusieurs processus PHP en parallèle
- Chaque processus a besoin de sa propre base de données de test isolée
- Le gain réel dépend du nombre de cœurs disponibles sur le runner de CI

18 minutes. C'est la durée qu'avait fini par atteindre une suite de tests d'intégration couvrant une extension de billetterie, avant qu'un travail de parallélisation ne soit engagé. Aucune régression de performance dans le code testé n'expliquait cette dérive : la suite s'était simplement enrichie, test après test, sur plusieurs mois, sans que personne ne remette en cause l'exécution strictement séquentielle des classes de test.

PHPUnit n'intègre pas nativement de mécanisme de parallélisation ; c'est le paquet `paratest`, construit par-dessus PHPUnit, qui répartit l'exécution des classes de test sur plusieurs processus PHP simultanés.

## Ce que paratest change concrètement

Installé comme dépendance de développement (`composer require --dev brianium/paratest`), paratest lance plusieurs processus PHP en parallèle, chacun exécutant une partie des classes de test de la suite, puis agrège les résultats dans un rapport unique. La commande de base ressemble à celle de PHPUnit, avec une option supplémentaire pour préciser le nombre de processus :

```
vendor/bin/paratest --processes=4 --configuration=phpunit.xml
```

Le gain observé dépend directement du nombre de cœurs disponibles sur la machine ou le runner de CI : sur une machine à quatre cœurs, la durée totale de la suite mentionnée plus haut est passée de 18 minutes à un peu moins de 6, sans qu'aucun test n'ait été retiré ni allégé.

## Le préalable indispensable : une base de données par processus

Le principal obstacle à la parallélisation d'une suite WordPress tient à la base de données. `WP_UnitTestCase` encapsule chaque test dans une transaction annulée en fin d'exécution, ce qui fonctionne bien en séquentiel, mais deux processus qui partagent la même base de données en parallèle finissent par se marcher dessus : verrous de table, données créées par l'un lues par l'autre, échecs intermittents impossibles à reproduire de façon stable.

La solution consiste à attribuer une base de données distincte à chaque processus, généralement via une variable d'environnement lue par le fichier de configuration des tests :

```
// wp-tests-config.php
$numero_processus = getenv('TEST_TOKEN') ?: '0';

define('DB_NAME', 'wordpress_test_' . $numero_processus);
```

> L'essentiel à retenir : paratest répartit les classes de test sur plusieurs processus PHP en parallèle ; Chaque processus a besoin de sa propre base de données de test isolée ; Le gain réel dépend du nombre de cœurs disponibles sur le runner de CI

paratest définit automatiquement une variable d'environnement `TEST_TOKEN` propre à chaque processus, ce qui permet de dériver un nom de base de données unique sans configuration manuelle supplémentaire. Il reste nécessaire de créer à l'avance autant de bases de données que de processus prévus, généralement dans une étape de préparation du pipeline de CI.

## Répartir les classes plutôt que les méthodes

Par défaut, paratest répartit les classes de test entre les processus, pas les méthodes individuelles. Une classe de test qui contient à elle seule un grand nombre de méthodes longues devient alors un goulot d'étranglement : le processus qui l'exécute peut rester actif bien après que les autres ont terminé leur lot. Repérer ces classes disproportionnées et les scinder en plusieurs classes plus petites améliore l'équilibrage global de la parallélisation, davantage qu'une simple augmentation du nombre de processus.

## Ce que la parallélisation ne résout pas

- Elle ne réduit pas le temps d'un test individuel lent : un test qui prend dix secondes reste un test de dix secondes, simplement exécuté en parallèle d'autres tests.
- Elle ne compense pas une suite mal isolée : des tests qui dépendent d'un ordre d'exécution précis échoueront de façon aléatoire une fois répartis entre plusieurs processus, ce qui révèle souvent un problème d'isolation préexistant, jusque-là masqué par l'exécution séquentielle.
- Elle augmente la consommation de ressources du runner de CI (mémoire, connexions à la base de données), un facteur à surveiller sur des offres d'intégration continue à capacité limitée.

## En résumé

Paralléliser une suite PHPUnit avec paratest permet de diviser significativement sa durée d'exécution sans réduire la couverture de tests, à condition de préparer une base de données isolée par processus et de surveiller l'équilibre de charge entre les classes de test. Le gain observé reste directement proportionnel au nombre de cœurs disponibles, ce qui en fait un investissement à évaluer au regard du runner de CI réellement utilisé.
