# Construire des objets de test complexes avec un Builder plutôt que des tableaux

> Quand les tableaux associatifs imbriqués deviennent illisibles pour préparer des données de test, le pattern Builder redonne de la clarté et de l'intention au code.

- Auteur : WordPress Développement
- Publié le : 2022-12-14
- Mis à jour le : 2022-12-14
- Catégorie : Tests
- URL : https://www.wpmoderne.fr/tests/builder-objets-test-complexes/

## L’essentiel

- Un Builder par entité complexe
- Des méthodes nommées plutôt que des clés de tableau
- Des valeurs par défaut sensées, à surcharger au cas par cas

`$commande = ['client' => ['adresse' => ['facturation' => ['pays' => 'FR', 'tva' => ['taux' => 20, 'exonere' => false]]]]];` — ce genre de ligne, une fois multiplié par quinze tests et complété par des variantes, transforme un fichier de tests en labyrinthe. Sur un projet de gestion de devis pour une coopérative agricole, ce tableau imbriqué comptait six niveaux de profondeur avant qu'on décide d'y remédier.

Le pattern Builder, bien connu en programmation orientée objet classique, s'applique très naturellement à la préparation de données de test. Il remplace la structure de données brute par une suite d'appels de méthode qui racontent ce que le test met en place.

## Le problème que pose le tableau imbriqué

Un tableau associatif imbriqué a deux défauts pour un test : il ne dit rien sur ce qui est important dans le scénario, et il oblige à répéter la structure entière pour changer une seule valeur profonde. Un développeur qui relit le test doit dérouler mentalement toute la structure pour comprendre que seule la TVA change entre deux cas.

Le tableau est aussi fragile : renommer une clé casse silencieusement tous les tests qui la construisent à la main, sans qu'aucune erreur de syntaxe ne le signale avant l'exécution.

## Construire un Builder minimal

> L'essentiel à retenir : Un Builder par entité complexe ; Des méthodes nommées plutôt que des clés de tableau ; Des valeurs par défaut sensées, à surcharger au cas par cas

Un Builder de test n'a pas besoin d'être générique ni configurable à l'infini. Il doit simplement exposer des méthodes nommées pour les variations que les tests utilisent réellement :

```
final class CommandeBuilder
{
    private string $paysFacturation = 'FR';
    private float $tauxTva = 20.0;
    private bool $exonereTva = false;
    private array $lignes = [];

    public function pourPaysFacturation(string $pays): self
    {
        $this->paysFacturation = $pays;
        return $this;
    }

    public function exoneree(): self
    {
        $this->exonereTva = true;
        return $this;
    }

    public function avecLigne(string $produit, float $prix, int $quantite = 1): self
    {
        $this->lignes[] = compact('produit', 'prix', 'quantite');
        return $this;
    }

    public function construire(): Commande
    {
        return new Commande(
            $this->paysFacturation,
            $this->tauxTva,
            $this->exonereTva,
            $this->lignes
        );
    }
}
```

Un test devient alors une phrase presque lisible à voix haute :

```
public function test_commande_exoneree_ne_facture_pas_de_tva(): void
{
    $commande = (new CommandeBuilder())
        ->exoneree()
        ->avecLigne('Panier de légumes', 18.50)
        ->construire();

    $this->assertSame(0.0, $commande->montantTva());
}
```

## Où placer la limite entre Builder et fixture WordPress

Sur un projet WordPress, le Builder ne remplace pas les fixtures fournies par `WP_UnitTestCase` pour créer des articles ou des utilisateurs ; il vient plutôt encapsuler la logique métier propre au projet, souvent portée par des classes de domaine indépendantes de WordPress. On construit l'objet métier avec le Builder, puis on l'attache éventuellement à un post ou une commande WooCommerce via les fixtures natives.

## Les valeurs par défaut, un choix qui compte

Le principal piège d'un Builder est de choisir des valeurs par défaut arbitraires. Elles doivent représenter le cas nominal du métier, celui qui revient le plus souvent dans les tests, pour que chaque test ne surcharge que ce qui le distingue réellement des autres.

- Un pays de facturation par défaut cohérent avec la majorité des clients réels du projet.
- Un taux de TVA standard, les exceptions étant explicitement nommées (`exoneree()`, `tauxReduit()`).
- Une liste de lignes vide par défaut, complétée explicitement par chaque test qui en a besoin.

> Un Builder qui a besoin de dix paramètres en constructeur n'a rien résolu, il a juste déplacé le problème d'un tableau vers une signature de méthode.

## Quand ne pas utiliser un Builder

Pour un objet simple à deux ou trois propriétés, un Builder ajoute plus de code qu'il n'en économise. La règle empirique retenue sur ce projet a été : au-delà de quatre champs optionnels combinables entre eux, le Builder devient rentable dès le troisième test qui l'utilise.

## En résumé

Remplacer un tableau imbriqué par un Builder ne change rien au comportement testé, mais tout à la lisibilité du test. Après refactorisation, la suite de tests de facturation de ce projet est passée de tableaux à six niveaux à des chaînes d'appels de deux ou trois méthodes, et le temps de relecture d'un test par un nouvel arrivant a nettement diminué.
