Une extension d’annuaire professionnel que nous maintenons propose un filtrage par catégorie, par ville et par statut d’adhésion. Combinés, ces trois filtres produisent des dizaines de variantes de WP_Query à vérifier : catégorie seule, ville seule, les deux ensemble, avec ou sans adhérent actif, etc. La première version de la suite comptait quatorze méthodes de test, presque toutes identiques à trois lignes près. Un fournisseur de données a permis de tout ramener à une seule méthode.
Le data provider PHPUnit répond exactement à ce besoin : dérouler un même scénario de test sur un ensemble de jeux de données, sans dupliquer la logique d’assertion.
Étape 1 — Identifier le squelette de test répété
Avant d’écrire le provider, il faut repérer ce qui varie réellement entre les méthodes existantes. Dans notre cas : les arguments passés à WP_Query, et le nombre de résultats attendus. Le reste — création des fixtures, appel à la requête, assertion sur le compte — était strictement identique.
public function test_filtre_categorie_seule(): void {
$requete = new WP_Query(['post_type' => 'membre', 'tax_query' => [...]]);
$this->assertCount(3, $requete->posts);
}
public function test_filtre_ville_seule(): void {
$requete = new WP_Query(['post_type' => 'membre', 'meta_query' => [...]]);
$this->assertCount(5, $requete->posts);
}
Étape 2 — Écrire le fournisseur de données
Un data provider est une méthode qui retourne un tableau de tableaux, chaque sous-tableau correspondant aux arguments d’un appel de la méthode de test. Depuis PHPUnit 9, on peut aussi utiliser des clés textuelles pour nommer chaque cas dans le rapport :
public static function scenarios_filtrage(): array {
return [
'categorie seule' => ['args' => ['tax_query' => [['taxonomy' => 'secteur', 'terms' => 'photographie']]], 'attendu' => 3],
'ville seule' => ['args' => ['meta_query' => [['key' => 'ville', 'value' => 'Lyon']]], 'attendu' => 5],
'categorie et ville' => ['args' => ['tax_query' => [['taxonomy' => 'secteur', 'terms' => 'photographie']], 'meta_query' => [['key' => 'ville', 'value' => 'Lyon']]], 'attendu' => 1],
'aucun filtre' => ['args' => [], 'attendu' => 9],
'ville inexistante' => ['args' => ['meta_query' => [['key' => 'ville', 'value' => 'Atlantide']]], 'attendu' => 0],
];
}
Étape 3 — Brancher le provider sur la méthode de test
Depuis PHPUnit 10, l’attribut #[DataProvider] remplace l’annotation @dataProvider historique, mais les deux syntaxes cohabitent selon la version du projet :
#[DataProvider('scenarios_filtrage')]
public function test_filtrage_annuaire(array $args, int $attendu): void {
$requete = new WP_Query(array_merge(['post_type' => 'membre'], $args));
$this->assertCount($attendu, $requete->posts);
}

Chaque entrée du tableau devient une exécution indépendante de test_filtrage_annuaire, avec son propre statut dans le rapport — un échec sur « ville et categorie » n’affecte pas les quatre autres cas, et le rapport nomme précisément lequel a échoué.
Étape 4 — Préparer les fixtures une seule fois
Le provider ne doit fournir que des données statiques (arguments et résultats attendus), jamais créer de contenu en base — il est exécuté avant même l’initialisation de WordPress dans certains contextes. Les fixtures correspondantes se créent donc classiquement dans setUp(), de façon à couvrir tous les cas du provider avec un seul jeu de données cohérent :
protected function setUp(): void {
parent::setUp();
$this->factory()->post->create(['post_type' => 'membre', 'meta_input' => ['ville' => 'Lyon']]);
// ... création des huit autres membres avec combinaisons ville/catégorie/statut
}
Combiner plusieurs providers pour multiplier les axes
Quand deux dimensions indépendantes doivent être croisées — par exemple le filtrage et le rôle de l’utilisateur qui exécute la requête — il est tentant d’ajouter un second #[DataProvider]. PHPUnit ne les combine pas automatiquement en produit cartésien : il faut le faire soi-même dans un seul provider, ou accepter deux méthodes de test distinctes si la logique diffère réellement selon le rôle :
- Un provider unique reste préférable tant que la logique testée est identique quel que soit l’axe.
- Deux providers séparés se justifient si les assertions elles-mêmes diffèrent selon l’axe testé.
- Un tableau généré dynamiquement (boucle imbriquée dans le provider) évite d’écrire à la main un produit cartésien de dix lignes.
Un data provider bien nommé documente à lui seul les règles métier du filtrage — nul besoin d’aller lire le code de l’extension pour comprendre ce qu’elle est censée faire.
En résumé
Les data providers ne réduisent pas seulement la duplication : ils rendent explicite la matrice de scénarios qu’un développeur doit avoir en tête en modifiant la logique de filtrage. Sur l’annuaire professionnel, faire passer quatorze méthodes à une seule a aussi eu un effet secondaire appréciable — ajouter un nouveau scénario de filtrage ne demande plus qu’une ligne dans le tableau, jamais une nouvelle méthode complète à écrire et maintenir.