MongoDBメモリサーバによるテスト


私は最近テストに深く飛び込んできた.私は合理的なコードの信頼性を持っているプロジェクトを作成するために時間を取っている.目標を作成し、適切にテストされ、まともなコードカバレッジを持っているアプリケーションを展開することです.
私が見つけたことの一つは、データベースをテストすることは簡単ではないということです.私が私のプロジェクトの1つでそうした方法の概要は、ここにあります.

バックストーリー🔙


私が取り組んでいるプロジェクトはOnLearn . それは本質的にeラーニング管理システムのためのPOCです.それは潜在的なユーザーがコースを置くか、コースを取ることができるプラットフォームとして機能します.UDEMY、Skallshare、またはMOOCプラットフォームのいずれか、実際には非常に似ています.
アプリケーションのスタックはノードです.J . MongoDB(マングースODM)、ビューのハンドルを使用します.Jestはテストフレームワークです.

問題🤔


それ自身を発表した最初の課題の一つはMongoDBのテストだった.私は、データベースロジックのために単位テストを書くことができました.
別の解決策を調べた後、私はMongoDBを見ている2つの記事に遭遇しました.
☝️ In-memory MongoDB for Testing .
✌️ そば
両方の記事ではnodkz's mongodb-memory-server パッケージ.
MongoDBメモリサーバとは
それは本当のMongoDBサーバを紡ぐパッケージです.これは、メモリ内のデータを格納するMongodプロセスを開始することができます.
メモリデータベースでは、アプリケーションのメインメモリ自体で、ラン、および閉じられます.彼らはハードドライブに触れることはないとして高速にすると、彼らが閉じて即座に破壊されるようにテストに適しています.

解決策💡


ここでは、どのようにMongoDBメモリサーバーは、私はOnLearningアプリケーションのモデルのいずれかのユニットテストを書くのに役立ちます:
1️⃣ Install dependencies .
2️⃣ Configure Jest .
3️⃣ Setup in-memory database .
4️⃣ Create a model .
5️⃣ Write unit tests .

1️⃣ 依存関係をインストールします。


以下のコマンドがインストールされますjest and mongodb-memory-server 同時に.
npm i jest mongodb-memory-server

2️⃣ Jestの設定


👉 テストスクリプト
加えるtest へのスクリプトpackage.json を返します.
"scripts": {
    "test": "jest --runInBand --detectOpenHandles",
}
CLIオプションの概要
  • "test" - テストを実行するためのスクリプト名を参照します.
  • jest - すべてのテストを実行する既定のコマンドです.
  • --runInBand - テストを実行する子プロセスのワーカープールを作成するのではなく、すべてのテストを現在のプロセスでシリアルに実行するコマンドです.
  • --detectOpenHandles - JESTがきれいに終了するのを防ぐオープンハンドルを収集して印刷しようとするコマンド.
  • 👉 テスト環境
    jestのデフォルト環境はjsdom. ノードアプリケーションでは、代わりにノードのような環境を指定する必要があります.
    "jest": {
        "testEnvironment": "node",
    }
    

    3️⃣ メモリデータベースのセットアップ。


    別のファイルはmongodb-memory-server 接続して切断する関数を使用します.
    // utils/test-utils/dbHandler.utils.js
    
    const mongoose = require('mongoose');
    const { MongoMemoryServer } = require('mongodb-memory-server');
    
    const mongoServer = new MongoMemoryServer();
    
    exports.dbConnect = async () => {
      const uri = await mongoServer.getUri();
    
      const mongooseOpts = {
        useNewUrlParser: true,
        useCreateIndex: true,
        useUnifiedTopology: true,
        useFindAndModify: false,
      };
    
      await mongoose.connect(uri, mongooseOpts);
    };
    
    exports.dbDisconnect = async () => {
      await mongoose.connection.dropDatabase();
      await mongoose.connection.close();
      await mongoServer.stop();
    };
    
    何が起こっているかを詳しく見る

    Import mongoose and mongodb-memory-server.

    
     const mongoose = require('mongoose');
     const { MongoMemoryServer } = require('mongodb-memory-> server');
    
    

    New mongodb-memory-server instance that will be used to run operations on in-memory db.

    
     const mongoServer = new MongoMemoryServer();
    
    

    A function to connect the in-memory database.

    
     exports.dbConnect = async () => {
       const uri = await mongoServer.getUri();
    
       const mongooseOpts = {
         useNewUrlParser: true,
         useCreateIndex: true,
         useUnifiedTopology: true,
         useFindAndModify: false,
       };
    
       await mongoose.connect(uri, mongooseOpts);
     };
    
    

    A function to disconnect the in-memory database.

    
     exports.dbDisconnect = async () => {
       await mongoose.connection.dropDatabase();
       await mongoose.connection.close();
       await mongoServer.stop();
     };
    
    

    4️⃣ モデルを作成します。


    ここでは、アプリケーションからユーザーモデルです.
    ユーザーはパスポートを使用して確認されますlocal & google 戦略
    このように、ユーザースキーマは以下を含みます:
  • local and google 認証データのフィールド.
  • profilePictureUrl ユーザーのアバター.
  • role ユーザーの型.
  • // database/models/user.model.js
    
    const { Schema, model } = require('mongoose');
    
    const userSchema = new Schema({
      local: {
        firstName: {
          type: String,
          trim: true,
        },
        lastName: {
          type: String,
          trim: true,
        },
        username: {
          type: String,
          trim: true,
          unique: true,
        },
        email: {
          type: String,
          match: [/^\S+@\S+\.\S+$/, 'Please use a valid email address.'],
          unique: true,
          lowercase: true,
          trim: true,
        },
        password: { type: String },
      },
      google: {
        id: String,
        token: String,
        email: String,
        name: String,
      },
      profilePictureUrl: {
        type: 'String',
        default: 'https://via.placeholder.com/150',
      },
      role: {
        type: String,
        enum: ['student', 'instructor', 'admin'],
        default: 'student',
      },
    });
    
    module.exports = model('User', userSchema);
    
    

    5️⃣ 書き込み単位のテスト。


    最後に、作成した操作を使用してmongo-memory-server 単体テスト
    ユーザーモデルがアプリケーションでテストされた方法の例を示します.備品やアサーションは別々のモジュールに配置されます.
    👉 器具
    // database/fixtures/index.js
    
    exports.fakeUserData = {
      firstName: 'Dummy',
      lastName: 'User',
      username: 'dummyUser',
      email: '[email protected]',
      password: '********',
      role: 'student',
    };
    
    👉 テストアサーションヘルパー
    // utils/test-utils/validators.utils.js
    
    exports.validateNotEmpty = (received) => {
      expect(received).not.toBeNull();
      expect(received).not.toBeUndefined();
      expect(received).toBeTruthy();
    };
    
    ...
    
    exports.validateStringEquality = (received, expected) => {
      expect(received).not.toEqual('dummydfasfsdfsdfasdsd');
      expect(received).toEqual(expected);
    };
    
    ...
    
    exports.validateMongoDuplicationError = (name, code) => {
      expect(name).not.toEqual(/dummy/i);
      expect(name).toEqual('MongoError');
      expect(code).not.toBe(255);
      expect(code).toBe(11000);
    };
    
    最後に、フィクスチャ、アサーションヘルパー、およびDB操作がテストで使用されます.🥳🥳🥳
    👉 ユーザモデルユニットテスト
    const User = require('../user.model');
    const { fakeUserData } = require('../../fixtures');
    const {
      validateNotEmpty,
      validateStringEquality,
      validateMongoDuplicationError,
    } = require('../../../utils/test-utils/validators.utils');
    const {
      dbConnect,
      dbDisconnect,
    } = require('../../../utils/test-utils/dbHandler.utils');
    
    beforeAll(async () => dbConnect());
    afterAll(async () => dbDisconnect());
    
    describe('User Model Test Suite', () => {
      test('should validate saving a new student user successfully', async () => {
        const validStudentUser = new User({
          local: fakeUserData,
          role: fakeUserData.role,
        });
        const savedStudentUser = await validStudentUser.save();
    
        validateNotEmpty(savedStudentUser);
    
        validateStringEquality(savedStudentUser.role, fakeUserData.role);
        validateStringEquality(savedStudentUser.local.email, fakeUserData.email);
        validateStringEquality(
          savedStudentUser.local.username,
          fakeUserData.username
        );
        validateStringEquality(
          savedStudentUser.local.password,
          fakeUserData.password
        );
        validateStringEquality(
          savedStudentUser.local.firstName,
          fakeUserData.firstName
        );
        validateStringEquality(
          savedStudentUser.local.lastName,
          fakeUserData.lastName
        );
      });
    
      test('should validate MongoError duplicate error with code 11000', async () => {
        expect.assertions(4);
        const validStudentUser = new User({
          local: fakeUserData,
          role: fakeUserData.role,
        });
    
        try {
          await validStudentUser.save();
        } catch (error) {
          const { name, code } = error;
          validateMongoDuplicationError(name, code);
        }
      });
    });
    
    
    合格テスト

    すべてのテストと実装を見つけることができますhere

    結論🏁


    最後にmongodb-memory-server パッケージは私のテストのためにたくさんのデータベースを重くしました.私はdbConnect and dbDisconnect 操作と私のアプリケーションのモデルをテストしてもservices associated with those models .
    私はあなたがこれについてどう思いますか?
    そして、これのためにどんな改善ヒントも共有するために自由に感じてください.✌️
    MongoDBメモリサーバリポジトリ👉 here 👈
    オンリーレポ👉 here 👈