---
title: "Des données de test qui disent la vérité"
description: "Les fixtures mentent par omission. mongoose-test-factory lit votre schéma et génère des données qui le respectent. Comment il décide quoi générer, et les trois modes que j'utilise chaque jour."
date: 2026-08-12
tags: ["mongodb", "tests", "typescript"]
language: fr
canonical: https://aissamirhir.com/fr/blog/test-data-that-tells-the-truth
source: aissamirhir.com
---
Chaque projet Mongoose que j'ai rejoint avait un dossier `fixtures/`, et chacun de ces dossiers était discrètement faux. Un champ ajouté au schéma mais pas au JSON. Un enum qui gagnait une valeur que personne ne testait. Un `required` qui apparaissait et la moitié des fixtures ne s'enregistraient plus, alors quelqu'un marquait le test `skip` et passait à autre chose.

Les fixtures sont un instantané de ce à quoi le schéma ressemblait le jour où elles ont été écrites. Le schéma, c'est ce que le code croit aujourd'hui. [mongoose-test-factory](https://www.npmjs.com/package/mongoose-test-factory) existe pour fermer cet écart : il lit le schéma et génère des données valides par rapport à lui, maintenant.

## Partir du schéma

Le paquet est un plugin. Appliquez-le, enveloppez le modèle, et le modèle obtient une `factory()`.

```typescript title="user.model.ts"
import mongoose, { Schema } from 'mongoose'
import mongooseTestFactory, { withFactory } from 'mongoose-test-factory'

const userSchema = new Schema({
  name: { type: String, required: true },
  email: { type: String, required: true, unique: true },
  age: { type: Number, min: 18, max: 120 },
  isActive: { type: Boolean, default: true },
})

userSchema.plugin(mongooseTestFactory)
export const User = withFactory(mongoose.model('User', userSchema))
```

```typescript
const user = User.factory().build()
// { name: 'John Doe', email: 'john.doe@example.com', age: 28, isActive: true }
```

Rien n'a été configuré. Le plugin a parcouru le schéma, vu une chaîne obligatoire nommée `name`, une chaîne unique nommée `email`, un nombre borné entre 18 et 120, et a généré en conséquence. Changez le schéma et la prochaine exécution change avec lui.

## Comment il décide quoi générer

Trois couches, par ordre de priorité.

1. **Un `factoryType` explicite** sur le champ l'emporte. Une quarantaine de types sont intégrés : `email`, `phone`, `price`, `slug`, `uuid`, `birthdate`, `tags`, etc.
2. **Le nom du champ.** `userEmail`, `contactEmail` et `email` ressemblent tous à des e-mails. `price`, `cost` et `amount` ressemblent à de l'argent. `createdAt` ressemble à un horodatage.
3. **Le type et ses validateurs.** Un `Number` avec `min` et `max` reste dans les bornes. Une `String` avec un `enum` pioche dedans. `required` est toujours respecté.

Quand le nom est ambigu, dites-le dans le schéma :

```typescript
const productSchema = new Schema({
  name: { type: String, factoryType: 'title' },
  vendor: { type: String, factoryType: 'company' },
  price: { type: Number, factoryType: 'price' },
  isActive: { type: Boolean, factoryType: 'active' }, // true environ 80 % du temps
  website: { type: String, factoryType: 'url' },
})
```

> [!TIP]
> Les noms de champs sémantiques paient deux fois. La factory les lit pour choisir un générateur, et le prochain ingénieur qui ouvre le fichier aussi. `email` bat `str1` pour les deux lecteurs.

## Trois modes, trois sortes de tests

| Méthode | Retourne | Touche la base | À utiliser pour |
|---|---|---|---|
| `build()` | objets simples | non | tests unitaires, corps de requête |
| `make()` | instances Mongoose | non | virtuels, méthodes, hooks |
| `create()` | documents enregistrés | oui | tests d'intégration |

```typescript
const body = User.factory().build() // rapide, aucune connexion requise
const doc = User.factory().make() // une instance, non persistée
const saved = await User.factory(50).create() // cinquante vraies lignes
```

La plupart de mes tests unitaires n'ouvrent jamais de connexion. `build()` est assez rapide pour que j'aie complètement arrêté de mettre en cache les données de test.

## Surcharges et relations

La valeur générée est un point de départ. Ce qui compte vraiment pour le test, fixez-le explicitement.

```typescript
const admin = User.factory().with({ name: 'Admin', role: 'admin' }).build()

const author = await User.factory().create()
const posts = await Post.factory(5).with({ author: author._id }).create()
```

Ce second exemple est le motif que j'utilise le plus : un vrai parent, N enfants générés qui pointent vers lui. La factory remplit tout ce que je n'ai pas mentionné, et les assertions se lisent comme une intention plutôt qu'une mise en place.

## Ce qu'il ne fera pas

Il n'inventera pas vos règles métier. Si le total d'une commande doit égaler la somme de ses lignes, le schéma ne le dit pas, et la factory ne peut pas le savoir. Écrivez cela comme une surcharge, ou comme un petit utilitaire dans votre configuration de test qui construit des commandes cohérentes. Des données générées doivent être valides par rapport au schéma et honnêtes sur le reste ; prétendre comprendre le domaine serait un autre genre de mensonge.
