Simulacre pour Mongoose
Introduction
Tester unitairement un projet utilisant Mongoose peut être un vrai casse-tête. En effet, Mongoose est un ORM pour MongoDB, et il est difficile de tester du code qui dépend d'une base de données. Cependant, il est possible de simuler Mongoose pour tester son code sans dépendre de MongoDB.
Simuler Mongoose
Pour simuler Mongoose, il suffit de créer un simulacre de Mongoose.
Commençons par créer un projet Node.js et installer Mongoose :
npm init -y
npm install mongoose
npm install --save-dev @jazim/mock-mongoose jasmine @types/jasmine typescript
Changer la configuration de TypeScript ainsi :
{
"compilerOptions": {
"target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
"module": "commonjs" /* Specify what module code is generated. */,
"outDir": "./dist" /* Specify an output folder for all emitted files. */,
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
"strict": true /* Enable all strict type-checking options. */,
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}
Initialiser Jasmine :
Changez la configuration de Jasmine ainsi :
{
"spec_dir": "dist/spec",
"spec_files": ["**/*[sS]pec.?(m)js"],
"helpers": ["helpers/**/*.?(m)js"],
"env": {
"stopSpecOnExpectationFailure": false,
"random": true
}
}
import mongoose, { Schema, model } from 'mongoose';
// **** Types **** //
export interface IFiche {
nom: string;
age: number;
adresse: string;
courriel: string;
telephone: string;
_id?: string;
}
// **** Schema **** //
const AnimalSchema = new Schema<IFiche>({
nom: { type: String, required: [true, 'Le nom est obligatoire'] },
age: {
type: Number,
required: [true, "L'âge est obligatoire"],
min: [1, "L'âge doit être plus grand que 0"],
},
adresse: {
type: String,
required: [true, "L'adresse est obligatoire"],
},
courriel: {
type: String,
required: [true, 'Le courriel est obligatoire'],
},
telephone: {
type: String,
required: [true, 'Le numéro de téléphone est obligatoire'],
validate: {
// Code inspiré de la documentation de Mongoose sur les validateurs personnalisés
// https://mongoosejs.com/docs/validation.html#custom-validators
validator: function (v: string) {
return /^(?:\+\d{1,3}\s?)?(?:\(\d{3}\)|\d{3})[-\s]?\d{3}[-\s]?\d{4}$/.test(
v
);
},
message: (props) =>
`${props.value} n'est pas un numéro de téléphone valide!`,
},
},
});
// **** Export **** //
mongoose.pluralize(null);
export default model<IFiche>('fiches', AnimalSchema);
Créez un fichier spec/test.spec.ts
:
import Fiche, { IFiche } from '../src/fiche';
// __tests__/user.test.js
const mockify = require('@jazim/mock-mongoose');
import mongoose, { Schema, model } from 'mongoose';
describe('Tester le modèle Fiche', () => {
it('findById doit retourner la fiche', () => {
const _fiche: IFiche = {
_id: '507f191e810c19729de860ea',
nom: 'Ted Mosby',
courriel: 'ted@mosbius-design.com',
adresse: '123, rue de la République',
age: 30,
telephone: '123-456-7890',
};
mockify(Fiche).toReturn(_fiche, 'findOne');
return Fiche.findById({ _id: '507f191e810c19729de860ea' }).then((doc) => {
expect(JSON.parse(JSON.stringify(doc))).toEqual(_fiche);
});
});
});
Modifier le fichier package.json
pour ajouter les scripts suivants :
{
"name": "simulacre_mongoose",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "tsc && jasmine"
},
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"mongoose": "^8.5.2"
},
"devDependencies": {
"@jazim/mock-mongoose": "^1.0.1",
"@types/jasmine": "^5.1.4",
"jasmine": "^5.2.0",
"ts-node": "^10.9.2",
"typescript": "^5.5.4"
}
}
La commande npm run test
exécute les tests Jasmine.