Aller au contenu

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
Initialiser TypeScript :

npx tsc --init

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 :

npx jasmine init

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
  }
}
Créer le modèle Mongoose (Fiche dans ce cas-ci):

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.