使用内存数据库测试 Node.js + Mongoose 内存数据库的优缺点让我们开始编码吧!

2025-05-28

使用内存数据库测试 Node.js + Mongoose

内存数据库的优缺点

让我们开始编码吧!

过去几周我一直在为Node.js 和 Mongoose应用程序创建单元测试,其中大部分逻辑由 mongoose 和 MongoDB 处理。

我尝试做的第一件事是创建模拟对象来匹配 Mongoose 中执行的每个操作及其不同的结果(起初这看起来是最合理的)。但进行到一半时,我开始意识到这太耗时了,而且如果查询语句发生变化怎么办?我是不是也得修改所有的模拟对象?

在谷歌搜索了一段时间后,我在 Github 上找到了这个包mongodb-memory-server,简单来说,它允许我们启动一个将数据存储在内存中的mongod进程。所以我决定尝试一下。

在本文中,我将向您介绍如何使用内存中的 MongoDB 进程来测试您的 Mongoose 逻辑,而无需创建任何模拟对象。
如果您想直接查看代码,我创建了一个Github 仓库,可以作为示例或样板。

内存数据库的优缺点

一开始我并不确定是否应该使用内存数据库来代替模拟,因此我做了一些调查,并列出了其优缺点:

优点:

  • 无需模拟:您的代码直接使用内存数据库执行,与使用常规数据库完全相同。
  • 更快的开发:鉴于我不需要为每个操作和结果构建模拟,而只需要测试查询,我发现开发过程更快、更直接。
  • 更可靠的测试:您正在测试将在生产中执行的实际代码,而不是一些可能不正确、不完整或过时的模拟代码。
  • 测试更容易构建:我不是单元测试专家,而且我只需要播种数据库并执行我需要测试的代码,这使得整个过程对我来说容易得多。

缺点:

  • 内存数据库可能需要播种
  • 更多的内存占用(dah)
  • 测试需要更长时间才能运行(取决于您的硬件)。

总之,内存数据库非常适合测试应用程序,其中逻辑主要通过数据库操作来处理,并且内存和执行时间不是问题。

让我们开始编码吧!

在这个例子中,我们将创建一个 Mongoose 模式和一个使用该模式执行一些操作的服务。
稍后我们将测试该服务执行的操作。

我们的项目完成后将会是这样的:

项目文件夹结构

1. 设置并安装依赖项

运行npm init以设置您的项目,不要担心测试脚本,稍后会处理它。

然后执行以下命令安装所有依赖项:



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


Enter fullscreen mode Exit fullscreen mode

注意:安装时,mongodb-memory-servermongod 二进制文件会下载并安装到 中node_modules/.cache。您还可以尝试其他选项,例如,mongodb-memory-server-global这将下载二进制文件到 中%HOME/.cache,以便测试其他项目。或者,mongodb-memory-server-core如果服务器启动时找不到二进制文件,则仅下载到 中。

选择最适合您需求的选项。

更多信息请访问github.com/nodkz/mongodb-memory-server

2.编写代码进行测试

现在我们将构建模型模式和稍后测试的服务。

2.a 产品架构



// src/models/product.js

const mongoose = require('mongoose');

/**
 * Product model schema.
 */
const productSchema = new mongoose.Schema({
    name: { type: String, required: true },
    price: { type: Number, required: true },
    description: { type: String }
});

module.exports = mongoose.model('product', productSchema);


Enter fullscreen mode Exit fullscreen mode

2.b 产品服务



// src/services/product.js

const productModel = require('../models/product');

/**
 * Stores a new product into the database.
 * @param {Object} product product object to create.
 * @throws {Error} If the product is not provided.
 */
module.exports.create = async (product) => {
    if (!product)
        throw new Error('Missing product');

    await productModel.create(product);
}


Enter fullscreen mode Exit fullscreen mode

3. 配置 jest

首先,我们将test脚本添加到package.json



"scripts": {
    "test": "jest --runInBand ./test"
}


Enter fullscreen mode Exit fullscreen mode

注意:该--runInBand参数将确保所有测试按顺序运行。我这样做是为了确保一次只运行一个 mongod 服务器。

最后将其添加到您的package.json,因为我们正在运行一个节点应用程序。



"jest": {
    "testEnvironment": "node"
}


Enter fullscreen mode Exit fullscreen mode

4.内存数据库处理

我编写了一个模块来执行一些基本操作,我将使用这些操作来处理内存数据库。



// tests/db-handler.js

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

const mongod = new MongoMemoryServer();

/**
 * Connect to the in-memory database.
 */
module.exports.connect = async () => {
    const uri = await mongod.getConnectionString();

    const mongooseOpts = {
        useNewUrlParser: true,
        autoReconnect: true,
        reconnectTries: Number.MAX_VALUE,
        reconnectInterval: 1000
    };

    await mongoose.connect(uri, mongooseOpts);
}

/**
 * Drop database, close the connection and stop mongod.
 */
module.exports.closeDatabase = async () => {
    await mongoose.connection.dropDatabase();
    await mongoose.connection.close();
    await mongod.stop();
}

/**
 * Remove all the data for all db collections.
 */
module.exports.clearDatabase = async () => {
    const collections = mongoose.connection.collections;

    for (const key in collections) {
        const collection = collections[key];
        await collection.deleteMany();
    }
}


Enter fullscreen mode Exit fullscreen mode

5. 编写一些测试

最后,我们使用以下代码测试我们的产品服务:



// tests/product.test.js

const mongoose = require('mongoose');

const dbHandler = require('./db-handler');
const productService = require('../src/services/product');
const productModel = require('../src/models/product');

/**
 * Connect to a new in-memory database before running any tests.
 */
beforeAll(async () => await dbHandler.connect());

/**
 * Clear all test data after every test.
 */
afterEach(async () => await dbHandler.clearDatabase());

/**
 * Remove and close the db and server.
 */
afterAll(async () => await dbHandler.closeDatabase());

/**
 * Product test suite.
 */
describe('product ', () => {

    /**
     * Tests that a valid product can be created through the productService without throwing any errors.
     */
    it('can be created correctly', async () => {
        expect(async () => await productService.create(productComplete))
            .not
            .toThrow();
    });
});

/**
 * Complete product example.
 */
const productComplete = {
    name: 'iPhone 11',
    price: 699,
    description: 'A new dual‑camera system captures more of what you see and love. '
};


Enter fullscreen mode Exit fullscreen mode

如果您想查看,repo中还有更多测试示例。

6. 尝试一下!

要尝试我们的新测试,只需npm test在终端👩‍💻中运行并观察您的测试是否生动!

文章来源:https://dev.to/paulasantamaria/testing-node-js-mongoose-with-an-in-memory-database-32np
PREV
在 Kubernetes 上部署应用程序:完整指南!
NEXT
你最后一个 MCP,用来安排你所有的社交帖子!🤯