3 min de lecture
Des données de test qui disent la vérité
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.
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 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().
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))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é.
- Un
factoryTypeexplicite sur le champ l'emporte. Une quarantaine de types sont intégrés :email,phone,price,slug,uuid,birthdate,tags, etc. - Le nom du champ.
userEmail,contactEmailetemailressemblent tous à des e-mails.price,costetamountressemblent à de l'argent.createdAtressemble à un horodatage. - Le type et ses validateurs. Un
Numberavecminetmaxreste dans les bornes. UneStringavec unenumpioche dedans.requiredest toujours respecté.
Quand le nom est ambigu, dites-le dans le schéma :
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' },
})#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 |
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 lignesLa 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.
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.
Ça vous a parlé ?
Conversation
Vous construisez quelque chose de ce genre ?
Dites-moi sur quoi vous travaillez. Je réponds sous un jour.