Гид по технологиям

Тестирование Mongoose-моделей с MongoDB Memory Server и Jest

5 min read Node.js Обновлено 04 Jan 2026
Тестирование Mongoose с MongoDB Memory Server
Тестирование Mongoose с MongoDB Memory Server

Используйте mongodb-memory-server, чтобы запускать реальную MongoDB в оперативной памяти для тестов Mongoose. Это даёт быстрые, изолированные интеграционные тесты без риска засорить реальную БД. В руководстве показано, как создать модель, настроить память, написать и прогонять тесты в Jest, а также даны советы по CI и отладке.

Прозрачные мерные чашки на светлом фоне

Что такое MongoDB Memory Server?

MongoDB Memory Server — это инструмент, который запускает полноценный процесс MongoDB, но хранит данные в памяти. Коротко:

  • Назначение: изолировать тестовую базу от реальной. По сути — «реальная» база без дисковых операций.
  • Польза: быстрее, безопаснее, удобно для CI/CD.
  • Ограничения: не подходит для тестирования сценариев, требующих специфического поведения хранения на диске, многоузловых кластеров, сложных операций с репликацией или сторедж-энджин-специфичных фич.

Определение терминов:

  • Mongoose — ODM (Object Data Modeling) для Node.js и MongoDB.
  • Jest — тестовый раннер и фреймворк утверждений от Facebook.

Когда это удобно и когда не годится

Важно: MongoDB Memory Server отлично подходит для интеграционных тестов моделей и запросов. Он плохо подходит, если вы тестируете:

  • ведение репликации и переключение мастера;
  • поведение на уровне файловой системы и драйвера (специфичные сторедж-энджины);
  • нагрузочное тестирование, где важен I/O на диск.

Создаём модель Mongoose

Мы будем работать с простой моделью «todo» с полями item и completed.

  1. Инициализируйте проект:
mkdir mongoose-model-test
cd mongoose-model-test
npm init -y
  1. Установите Mongoose:
npm install mongoose
  1. Создайте файл todo.model.js и определите схему. Ниже — корректная и рабочая версия:
const mongoose = require("mongoose");
const { Schema } = mongoose;

const TodoSchema = new Schema({
  item: {
    type: String,
    required: true
  },
  completed: {
    type: Boolean,
    required: true
  }
});

module.exports = mongoose.model('Todo', TodoSchema);

Пояснение: поле completed было в исходном тексте с опечаткой. Здесь оно исправлено — это критично для корректной валидации.

План тестов

Минимальный набор проверок для модели:

  • Успешная запись валидного документа.
  • Ошибка при отсутствии обязательного поля.
  • Ошибка при неверном типе данных для поля.

Эти тесты покрывают базовую валидацию схемы.

Настройка MongoDB Memory Server для тестов

Установите пакет:

npm install mongodb-memory-server --save-dev

Создайте файл setupTestDB.js (имя важно — используйте единообразно при импорте) и добавьте логику запуска/остановки инстанса и очистки коллекций. Корректный пример:

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

let mongo = null;

const connectDB = async () => {
  mongo = await MongoMemoryServer.create();
  const uri = mongo.getUri();

  await mongoose.connect(uri, {
    // опции Mongoose 7+ могут не требовать явных флагов, но указаны здесь для совместимости
    useNewUrlParser: true,
    useUnifiedTopology: true
  });
};

const dropDB = async () => {
  if (mongo) {
    await mongoose.connection.dropDatabase();
    await mongoose.connection.close();
    await mongo.stop();
  }
};

const dropCollections = async () => {
  if (mongo) {
    const collections = await mongoose.connection.db.collections();
    for (let collection of collections) {
      // .deleteMany({}) безопаснее, чем deprecated .remove() в современных версиях
      await collection.deleteMany({});
    }
  }
};

module.exports = { connectDB, dropDB, dropCollections };

Важно: в исходном руководстве использовались устаревшие методы и несогласованность имён файлов. В примере выше мы используем deleteMany({}) вместо remove() и единое имя файла setupTestDB.js.

Конфигурация Jest

Установите Jest:

npm install --save-dev jest

В package.json замените scripts и добавьте конфигурацию jest:

"scripts": {
  "test": "jest --runInBand --detectOpenHandles"
},
"jest": {
  "testEnvironment": "node"
},

Пояснение параметров:

  • –runInBand — запуск тестов в одном процессе, полезно при использовании in-memory-базы.
  • –detectOpenHandles — помогает выявить открытые подключения, если тесты не завершаются.

Пишем тесты (todo.model.test.js)

Создайте файл todo.model.test.js и импортируйте зависимости:

const mongoose = require('mongoose');
const { connectDB, dropDB, dropCollections } = require('./setupTestDB');
const Todo = require('./todo.model');

Добавьте хуки Jest для подключения и очистки:

beforeAll(async () => {
  await connectDB();
});

afterAll(async () => {
  await dropDB();
});

afterEach(async () => {
  await dropCollections();
});

Теперь сами тесты. Ниже — три теста: успешное создание, отсутствие required-поля и неверный тип.

describe('Todo Model', () => {
  it('should create a todo item successfully', async () => {
    let validTodo = {
      item: 'Do the dishes',
      completed: false
    };

    const newTodo = new Todo(validTodo);
    await newTodo.save();

    expect(newTodo._id).toBeDefined();
    expect(newTodo.item).toBe(validTodo.item);
    expect(newTodo.completed).toBe(validTodo.completed);
  });

  it('should fail for todo item without required fields', async () => {
    let invalidTodo = {
      item: 'Do the dishes'
      // отсутствует completed
    };

    try {
      const newTodo = new Todo(invalidTodo);
      await newTodo.save();
    } catch (error) {
      expect(error).toBeInstanceOf(mongoose.Error.ValidationError);
      expect(error.errors.completed).toBeDefined();
    }
  });

  it('should fail for todo item with fields of wrong type', async () => {
    let invalidTodo = {
      item: 'Do the dishes',
      completed: 'False' // строка вместо булева
    };

    try {
      const newTodo = new Todo(invalidTodo);
      await newTodo.save();
    } catch (error) {
      expect(error).toBeInstanceOf(mongoose.Error.ValidationError);
      expect(error.errors.completed).toBeDefined();
    }
  });
});

Пояснение: мы используем try/catch, чтобы поймать ValidationError и проверить, что ошибка связана с ожидаемым полем.

Приёмочные критерии

  • Валидный документ сохраняется и получает _id.
  • При отсутствии обязательного поля сохраняться не должен и выбрасывается mongoose.ValidationError.
  • При неверном типе поля сохраняться не должен и выбрасывается mongoose.ValidationError.

Отладка и часто встречаемые проблемы

  1. Тесты зависают после выполнения. Частая причина — не закрытое подключение к MongoDB. Убедитесь, что послеAll вызывается dropDB и mongo.stop(). Используйте –detectOpenHandles для диагностики.
  2. Несоответствие имён файлов при импорте. Держите имена файлов и экспорты в синхронизации (например, setupTestDB.js и ./setupTestDB).
  3. Различия версий Mongoose. Опции подключения (useNewUrlParser и useUnifiedTopology) могут быть уже не обязательны в новых версиях Mongoose; проверьте документацию вашей версии.

Important: в CI иногда МongoMemoryServer скачивает бинарники при первом запуске. Кэшируйте бинарники в CI, чтобы ускорить билды (например, в GitHub Actions сохраните ~/.cache/mongodb-binaries).

Альтернативные подходы

  • Mocking Mongoose: быстрый, но не покрывает реальную работу драйвера и схемы.
  • Отдельный локальный MongoDB контейнер (docker-compose): ближе к проду, но медленнее и сложнее в CI.
  • Testcontainers (контейнеры Docker для тестов): гарантирует среду, близкую к продакшен, но требует Docker в CI.

Ментальная модель выбора:

  • Unit-tests: мокайте отдельные функции (без реальной БД).
  • Integration-tests: mongodb-memory-server — лёгкий и быстрый выбор.
  • E2E: контейнеры/реальная инфраструктура.

Чек-лист по ролям

Разработчик:

  • Создал корректную схему Mongoose.
  • Написал тесты на позитивные и негативные сценарии.
  • Убедился в закрытии подключений.

Код-ревьювер:

  • Проверил корректность типов и required-полей.
  • Проверил, что в тестах нет обращения к реальной базе.

CI-инженер:

  • Настроил кэш для mongodb-binaries.
  • Убедился, что runner поддерживает необходимые флаги Docker (если используются контейнеры).

Snippets / Cheatsheet

  • Запустить только один тест-файл:
npm test -- todo.model.test.js
  • Быстро запустить Jest в watch-режиме (локально):
npx jest --watch
  • Очистка коллекций (внутри setup файла):
for (let collection of collections) {
  await collection.deleteMany({});
}

Совместимость и миграции

  • Если вы обновляете Mongoose, проверьте изменения API подключения и поведения схем.
  • MongoMemoryServer загружает бинарники MongoDB. Для воспроизводимости тестов фиксируйте версию пакета в package.json.

Безопасность и конфиденциальность

Тестовые данные в памяти не попадают в реальную базу. Тем не менее:

  • Не храните реальные персональные данные в тестовых наборах.
  • Если вы подаёте фикстуры, убедитесь в их анонимности.

Примеры, когда такой подход не подойдёт

  • Тестирование транзакций в шардированной/реплицированной топологии.
  • Низкоуровневые тесты драйвера, зависящие от конкретной версии mongod и его опций.

Краткое резюме

MongoDB Memory Server — простой и мощный инструмент для тестирования Mongoose-моделей. Он даёт изолированную, быструю и безопасную среду для интеграционных тестов. Используйте его для проверки валидации схем, CRUD-операций и бизнес-логики, но выбирайте другие подходы для тестов, чувствительных к реальному поведению дисковой подсистемы или кластеризации.

Notes: поддерживайте единообразие имён файлов и корректные экспорты. В CI кэшируйте бинарники MongoDB, чтобы избежать длительных загрузок при каждом запуске.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

RDP: полный гид по настройке и безопасности
Инфраструктура

RDP: полный гид по настройке и безопасности

Android как клавиатура и трекпад для Windows
Гайды

Android как клавиатура и трекпад для Windows

Советы и приёмы для работы с PDF
Документы

Советы и приёмы для работы с PDF

Calibration в Lightroom Classic: как и когда использовать
Фото

Calibration в Lightroom Classic: как и когда использовать

Отключить Siri Suggestions на iPhone
iOS

Отключить Siri Suggestions на iPhone

Рисование таблиц в Microsoft Word — руководство
Office

Рисование таблиц в Microsoft Word — руководство