Тестирование Mongoose моделей с mongodb-memory-server и Jest

Зачем тестировать модели в памяти
При интеграционном тестировании Mongoose моделей важно не трогать реальную базу данных проекта: фейковые записи могут загрязнить рабочие данные, тесты в облаке становятся медленными или дорогими, а настройка локальной БД на CI усложняет конвейер. mongodb-memory-server запускает реальный экземпляр MongoDB в памяти: база ведёт себя как «настоящая», но данные не пишутся на диск и исчезают после остановки сервера. Это делает тесты быстрыми, детерминированными и безопасными для реальной БД.
Важно: mongodb-memory-server не заменяет все виды тестов — он отлично подходит для unit/integration тестов моделей и логики доступа к данным, но не для проверки сетевой конфигурации, production-оптимизаций или распределённого поведения.
Что понадобится перед началом
- Node.js (LTS) и npm/yarn
- Базовые знания Mongoose и Jest
- Проектная папка для экспериментов
Совет: запускать тесты локально и в CI с ключом –runInBand (как в примере) удобно для упрощённой отладки подключения к MongoMemoryServer.
План результата
Мы реализуем:
- Mongoose модель Todo (поля item и completed).
- Три набора тестов: успешное сохранение, отсутствие требуемого поля, неправильный тип поля.
- Файлы: package.json, todo.model.js, setupdb.js, todo.model.test.js
Ниже пошаговая инструкция с кодом, пояснениями и набором практических приёмов.
Создание проекта и установка зависимостей
Откройте терминал, создайте папку и перейдите в неё:
`mkdir mongoose-model-test
cd mongoose-model-test
`
Инициализируйте npm:
`npm init -y
`
Установите mongoose:
`npm install mongoose
`
Установите mongodb-memory-server:
`npm install mongodb-memory-server
`
Установите jest для тестов:
`npm install jest
`
В package.json добавьте/замените блок scripts и настройку окружения для jest:
`"scripts": {
"test": "jest --runInBand --detectOpenHandles"
},
"jest": {
"testEnvironment": "node"
},
`
Модель Mongoose (todo.model.js)
Создайте файл todo.model.js со схемой для задач (todo). Схема ожидает поле item (String) и completed (Boolean) — оба поля обязательны.
`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)
`
Пояснение: модель используется как фабрика документов и даёт API (.save(), .find(), .findById() и т. д.). Объявление required помогает ловить ошибки валидации ещё на уровне модели.
Настройка in-memory базы (setupdb.js)
Создайте файл setupdb.js. В нём — код для поднятия MongoMemoryServer, подключения Mongoose и очистки между тестами.
`const mongoose = require("mongoose");
const { MongoMemoryServer } = require("mongodb-memory-server");
`
Функция подключения (connectDB):
`let mongo = null;
const connectDB = async () => {
mongo = await MongoMemoryServer.create();
const uri = mongo.getUri();
await mongoose.connect(uri, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
};
`
Функция остановки и удаления базы (dropDB):
`const dropDB = async () => {
if (mongo) {
await mongoose.connection.dropDatabase();
await mongoose.connection.close();
await mongo.stop();
}
};
`
Функция очистки коллекций после каждого теста (dropCollections):
`const dropCollections = async () => {
if (mongo) {
const collections = await mongoose.connection.db.collections();
for (let collection of collections) {
await collection.deleteMany({});
}
}
};
`
Экспорт:
`module.exports = { connectDB, dropDB, dropCollections}
`
Важно: мы используем deleteMany({}) вместо устаревшего collection.remove() — это современная и безопасная операция очистки.
Написание тестов (todo.model.test.js)
Создайте файл todo.model.test.js. В нём подключаем Mongoose, модель и функции из setupdb.js:
`const mongoose = require("mongoose");
const { connectDB, dropDB, dropCollections } = require("./setupdb");
const Todo = require("./todo.model");
`
Подключение/очистка тестовой БД через Jest хуки beforeAll/afterEach/afterAll:
`beforeAll(async () => {
await connectDB();
});
afterAll(async () => {
await dropDB();
});
afterEach(async () => {
await dropCollections();
});
`
Тестовый блок и тесты — позитивный и два негативных:
`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",
};
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();
}
});
});
`
Пояснение: в негативных тестах мы ожидаем mongoose.Error.ValidationError — это стандартная ошибка валидации модели Mongoose.
Запуск тестов
Выполните команду:
npm testJest запустит тесты, подняв MongoMemoryServer, подключившись и очистив данные между тестовыми случаями.
Отладка и типичные проблемы
- Тесты висят/не завершатся: попробуйте добавить –runInBand (в примере уже есть) или убедитесь, что в afterAll вы корректно вызываете mongoose.connection.close() и mongo.stop().
- Ошибки подключения: убедитесь, что версия mongodb-memory-server совместима с вашей версией Node.js и mongoose.
- Миграции/индексы: mongodb-memory-server поднимает чистую БД — индексы автоматически создаются при первом обращении к модели, но если вы рассчитываете на внешние миграции, настройте их в beforeAll.
Important: не используйте in-memory БД для проверки долгоживущих оптимизаций индексирования или поведения при отказах кластера — это другие уровни тестирования.
Когда подход не подходит (примеры)
- Тестирование распределённого поведения (репликация, шардирование) — mongodb-memory-server эмулирует одиночный экземпляр.
- Тесты, зависящие от файловой системы MongoDB или внешних инструментов мониторинга.
- Проверка производительности под нагрузкой — in-memory БД часто быстрее и не отражает real-world I/O.
Альтернативные подходы
- Локальный контейнер MongoDB (Docker) — даёт более похожую на прод среду, но медленнее и требует управления контейнерами.
- Тестовая облачная БД — подходит при необходимости близости к прод, но дороже и сложнее в автоматизации.
- Подмены/моки для Mongoose моделей — быстры, но вы не проверяете реальную валидацию и поведение драйвера.
Практическая мини-методика для команды
- Подключите mongodb-memory-server в рамках тестовой среды (setupdb.js).
- Пишите тесты, которые проверяют валидацию схемы, сохранение и чтение.
- Очистку коллекций выполняйте после каждого теста (deleteMany) — это гарантирует изоляцию.
- Выключайте сервер и закрывайте соединения в afterAll.
- На CI используйте параметр –runInBand при необходимости последовательного выполнения.
Роли и чек-листы
Разработчик:
- Добавить тесты для позитивных и негативных сценариев.
- Проверить, что коллекции очищаются между тестами.
- Не оставлять реальные строки подключения в тестах.
Ревьюер:
- Убедиться, что ошибки валидации явно проверяются (toBeInstanceOf).
- Проверить корректность типов в тестовых данных.
Инженер CI:
- Настроить кеш npm для ускорения сборок.
- Убедиться, что CI-образ содержит совместимую версию Node.js.
Критерии приёмки
- Все тесты в jest проходят локально и в CI.
- Нет потерянных подключений (нет висящих процессов afterAll).
- Коллекции очищаются между тестами.
Тестовые случаи и критерии приёмки
- Успешное сохранение
- Вход: объект с item: String, completed: Boolean
- Ожидается: newTodo._id определён, поля совпадают
- Отсутствует обязательное поле
- Вход: объект без completed
- Ожидается: ValidationError с error.errors.completed
- Неверный тип поля
- Вход: completed: “False”
- Ожидается: ValidationError
Советы по безопасности и приватности
- В тестах не храните реальные персональные данные. Если требуется тестировать на похожих данных, используйте обезличенные или синтетические наборы.
- Проверьте, что конфигурация CI не публикует логи с чувствительной информацией о подключении.
Совместимость и миграции
- mongodb-memory-server активно развивается и может иметь breaking changes между мажорными версиями. Перед обновлением проверьте changelog и тесты.
- Mongoose также меняет настройки подключения: опции useNewUrlParser и useUnifiedTopology были рекомендованы для «старых» версий. В новых версиях они могут быть не обязательны — смотрите документацию вашей версии.
Набор типичных ошибок и их решения
Ошибка: ReferenceError: MongoMemoryServer is not defined Решение: Убедитесь, что импорт { MongoMemoryServer } написан корректно и что пакет установлен.
Ошибка: EADDRINUSE при старте MongoMemoryServer Решение: Проверьте, не запущен ли другой Mongo на том же порту; mongodb-memory-server обычно выбирает свободный порт, но в редких условиях конфликт возможен.
Небольшой чек-лист для добавления новых тестов моделей
- Есть позитивный сценарий сохранения
- Есть негативные сценарии для required и типов
- Очистка коллекций после каждого теста
- Закрытие соединений в afterAll
- Тесты детерминированы и не зависят от внешних сервисов
Короткий глоссарий (1 строка)
- Mongoose — ODM для MongoDB в Node.js.
- mongodb-memory-server — npm-пакет, запускающий MongoDB в памяти.
- Jest — тестовый раннер для JavaScript/Node.js.
Краткое объявление для команды (100–200 слов)
В проект добавлен шаблон для тестирования Mongoose моделей с помощью mongodb-memory-server и Jest. Это позволит нам запускать быстрые, изолированные тесты моделей без риска повредить рабочую базу. В репозитории появился пример с моделью todo, настройкой setupdb.js и тестами todo.model.test.js. Инструкция включает сценарии успешного сохранения, проверки отсутствующих обязательных полей и проверки типов. Пожалуйста, используйте этот шаблон при добавлении тестов для новых моделей и убедитесь, что они выполняются в CI.
Соц-превью
OG title: Тестирование Mongoose с mongodb-memory-server
OG описание: Быстрое и безопасное тестирование Mongoose моделей в памяти с примерами на Jest.
Итоги
- mongodb-memory-server делает тесты моделей быстрыми и безопасными, поднимая реальную MongoDB в памяти.
- В примере показано создание модели, настройка in-memory БД и три ключевых теста.
- Используйте предложенные хуки и чек-листы, чтобы тесты были детерминированными и чистыми.
Спасибо — если нужно, могу подготовить готовый репозиторий с этими файлами или добавить дополнительные тесты (например, для индексов, виртуальных полей или middleware).
Похожие материалы
Будильники и таймеры на HomePod — как настроить
Disk Utility на Mac: проверка, ремонт и форматирование
Мышь для Mac: подключение, настройка и отладка
Пакетная обрезка и изменение размера в Lightroom
Команда head в Linux — руководство и примеры