JestとMockingooseでマングースモデルをテストする方法



序論
あなたがユニットテストに導入されているとき、ほとんどの場合、単体テストと、おそらく、テスト、統合テスト、E 2 Eテストの違いを説明している有名なピラミッドを示す簡単な説明の後、あなたは最初のテストで提示されます.
// sum.js
const add = (a, b) => {
  return a + b;
}

module.exports = {
  add,
};

// sum.test.js
const { add } = require('./sum');

it('should sum numbers', () => {
  expect(add(1, 2)).toBe(3);
}
上記のテストは明確でわかりやすいですが、実際には、これは多くの場合に適用することができますが、物事は非常に複雑になるときに依存関係、APIの呼び出しなどのようなモックを開始する必要があります..
そして、非常にトリッキーなケースは、データベースに対していくつかのCRUD操作を行うような、マングースのようなODMからいくつかのモデルを呼び出す機能をテストする必要があるときです.
場合によっては、実際のテストデータベースを使用することもできます.これに関する唯一の問題は、単体テストを実行するためにデータベースを使用する必要があると仮定していることです.
もう一つの解決策は、メモリだけで、そして、テストの期間の間だけで生きるデータベースを使うことができましたmongodb-memory-server パッケージ.
しかし、これがほとんどの場合、ほとんどの場合は、任意のCI/CDにコードを展開する場合は問題が発生する可能性があります(と私は!).
また、単体テストの重要な要因は、たとえばE 2 Eテストとは異なり、外部サービスに依存してはいけないことです.
あなたの目標は、機能をテストし、それよりも深くないようにする必要があります.

MockingOseによる問題解決

必要条件
  • Jestを使用する方法を既に知っている
  • あなたは既にホットマングースモデルを知っている
  • どのようにノードの良い知識があります.エクスプレスのようなフレームワークを持つjs.アプリケーションワークス

  • モデル
    それで、私たちには2、3のモデル(古典的な本と作者)があると言いましょう、そして、私たちの本モデルはこのように見えます:
    // models/books.js
    
    const mongoose = require('mongoose');
    const Schema = mongoose.Schema;
    
    const BooksSchema = new Schema({
      title: {
        type: String,
        trim: true
      },
      author: {
        type: Schema.Types.ObjectId,
        ref: 'authors'
      },
      year: {
        type: String,
      }
    });
    
    module.exports = mongoose.model('books', BooksSchema);
    

    サービス
    それで、しばしば、あなたが終点を持っている経路とその終点がどのように解決されるか、モデルを呼んで、データを取り出して、応答を返すルートがある例をしばしば見ます.
    ここでの問題は、あなたがルータから離れてロジックを抽象化したいからです.そして、いろいろな理由のために、巨大なファイルを持つのを避けるように、コードを乾燥させておいてください、そして、おそらくAPIエンドポイントのためのリゾルバとしてだけでなく、異なった文脈で同じコードを再利用するでしょう.
    私は詳細にあまり行きません、しかし、私が通常するものは私のAPIの特定のモジュールのためにいろいろなルートをリストするルータファイルを持っていることです、各々のルートはコントローラを呼びます、そして、コントローラはサービスを呼びます.このルートはXをしたいというブリッジで、データをサービスに依頼して、レスポンスをルートに戻します.
    そして、FETCHのようなコアロジックは、サービスの中で生きます.そして、それはモデルを照会しなければならなくて、データを返します.
    それで私の本サービスは次のようになります.
    // services/books.js
    
    const Books = require('../models/books');
    
    const fetchBooks = () => Books
      .find({})
      .populate('author')
      .exec();
    
    const fetchBook = id => Books
      .findById(id)
      .populate('author')
      .exec();
    
    const createBook = ({title, author, year}) => {
      const book = new Books({
        title,
        author,
        year,
      });
      return book.save();
    }
    
    module.exports = {
      fetchBooks,
      fetchBook,
      createBook,
    };
    
    

    Note: the code above is brutally minimal just to keep the example as lean as possible.


    ご覧のように、私たちのサービスは書籍モデルを含みます.そして、それはデータベースODMの操作をするためにそれを使用します.

    サービスのテスト

    インストール
    最初にインストールすることですmockingoose with npm i mockingoose -D .

    テストの作成
    今テストファイルを作成したいbooks.test.js .
    次に、mockingoose ,モデルとファイルにテストする関数をインポートする必要があります.
    const mockingoose = require('mockingoose');
    const BooksModel = require('../models/books');
    const {
      fetchBooks,
      fetchBook,
      createBook,
    } = require('./books');
    
    今、魔法を起こすために、我々のモデルを包む必要がありますmockingoose , そして、模造されたモデルに、例えば、あなたが本のリストを返したいならば、返すべきものを教えてください.
    mockingoose(BooksModel).toReturn([
      {
        title: 'Book 1',
        author: {
          firstname: 'John',
          lastname: 'Doe'
        },
        year: 2021,
      },
      {
        title: 'Book 2',
        author: {
          firstname: 'Jane',
          lastname: 'Doe'
        },
        year: 2022,
      }
    ], 'find');
    
    できるだけtoReturn 関数は2つの値を予想します.最初のものはモデルを返すデータです.2番目のものはどのような操作ですかfind , findOne , update , などそして、我々のケースでは、我々はfind 我々は本のリストを取得する必要があります.
    それで、本を取り出すための完全なテストは次のようになります.
    // books.test.js
    
    const mockingoose = require('mockingoose');
    const BooksModel = require('../models/books');
    const {
      fetchBooks,
      fetchBook,
      createBook,
    } = require('./books');
    
    describe('Books service', () => {
      describe('fetchBooks', () => {
        it ('should return the list of books', async () => {
          mockingoose(BooksModel).toReturn([
            {
              title: 'Book 1',
              author: {
                firstname: 'John',
                lastname: 'Doe'
              },
              year: 2021,
            },
            {
              title: 'Book 2',
              author: {
                firstname: 'Jane',
                lastname: 'Doe'
              },
              year: 2022,
            }
          ], 'find');
          const results = await fetchBooks();
          expect(results[0].title).toBe('Book 1');
        });
      });
    });
    
    同様にfetchbookメソッドをテストしたい場合は、1つのドキュメントだけを取得します.
    describe('fetchBook', () => {
      it ('should return a book', async () => {
        mockingoose(BooksModel).toReturn(
          {
            _id: 1,
            title: 'Book 1',
            author: {
              firstname: 'John',
              lastname: 'Doe'
            },
            year: 2021,
          }, 'findOne');
        const results = await fetchBook(1);
        expect(results.title).toBe('test');
      });
    });
    
    このライブラリの良いことは、あなたがchinit操作を呼び出すならば、それもサポートするということですexec or populate たとえば、あなたはそれらについて心配する必要はありません.

    テストを実行する
    あなたがテストを実行するならばnpm run test , あなたの拳テストを正常に実行してください.


    最後の思考
    あなたの本当の世界のアプリケーションをテストすることができますときに、特にアプリケーションのデータのほとんどをモッキングで迷子になるが、ツールのようなmockingoose 私の人生は非常に簡単になり、それはCi/CDでもうまく動作します!
    このライブラリの使い方の詳細についてはgithub project page .