gpt4 book ai didi

javascript - 如何创建一个可模拟的类来连接到 mongoDB?

转载 作者:数据小太阳 更新时间:2023-10-29 04:47:06 26 4
gpt4 key购买 nike

我已经尝试创建一个类来连接到 mongoDB(并使用 (gridfs-stream) 获得 gridFS 连接)。但是我确实遇到了两个问题:

  1. 我有时会收到 mongo 错误 server instance in invalid state connected
  2. 我不可能模拟这个类 - 使用 jestJS

所以如果有人可以帮助我优化这个类以获得一个真正扎实的工作类,我将非常感激。例如,我不喜欢 connect() 函数中的 let that = this

Example repo

数据库类

const mongo = require('mongodb')
const Grid = require('gridfs-stream')
const { promisify } = require('util')

export default class Db {
constructor (uri, callback) {
this.db = null
this.gfs = null
const server = process.env.MONGO_SERVER || 'localhost'
const port = process.env.MONGO_PORT || 27017
const db = process.env.MONGO_DB || 'test'

// Is this the correct way to connect (using mongo native driver)?
this.connection = new mongo.Db(db, new mongo.Server(server, port))
this.connection.open = promisify(this.connection.open)
this.connected = false
return this
}

async connect (msg) {
let that = this
if (!this.db) {
try {
await that.connection.open()
that.gfs = Grid(that.connection, mongo)
this.connected = true
} catch (err) {
console.error('mongo connection error', err)
}
}
return this
}

isConnected () {
return this.connected
}
}

示例

此函数将使用上面的类向数据库添加一个新用户:

import bcrypt from 'bcrypt'
import Db from './lib/db'
const db = new Db()

export async function createUser (obj, { username, password }) {
if (!db.isConnected()) await db.connect()
const Users = db.connection.collection('users')
return Users.insert({
username,
password: bcrypt.hashSync(password, 10),
createdAt: new Date()
})
}

单元测试

我需要创建一个单元测试来测试是否调用了 mongoDB 方法。没有用于测试该方法的集成测试。所以我需要模拟数据库连接、收集和插入方法。

import bcrypt from 'bcrypt'
import { createUser } from '../../user'

import Db from '../../lib/db'
const db = new Db()
jest.mock('bcrypt')

describe('createUser()', () => {
test('should call mongoDB insert()', async () => {
bcrypt.hashSync = jest.fn(() => SAMPLE.BCRYPT)
// create somekind of mock for the insert method...
db.usersInsert = jest.fn(() => Promise.resolve({ _id: '507f1f77bcf86cd799439011' }))
await createUser({}, {
username: 'username',
password: 'password'
}).then((res) => {
// test if mocked insert method has been called
expect(db.usersInsert).toHaveBeenCalled()
// ... or better test for the returned promise value
})
})
})

最佳答案

有多种方法可以解决这个问题。我将列出其中的一些

  • 使用 Jest 手动模拟模拟数据库类。如果您使用太多 mongo 函数,这可能会很麻烦。但是由于您是通过 DB 类封装大部分内容,因此它可能仍然是可管理的
  • 使用模拟的 mongo 实例。 This项目允许您模拟 MongoDB 并使用 js 文件持久化数据
  • 使用 in-memory数据库
  • 使用实际的 mongodb

我将在此处展示第一个案例,您发布了该案例以及代码以及如何使其发挥作用。所以我们要做的第一件事是更新 __mocks__/db.js文件到下面

jest.mock('mongodb');
const mongo = require('mongodb')
var mock_collections = {};
var connectError = false;
var connected = false;
export default class Db {
constructor(uri, callback) {
this.__connectError = (fail) => {
connected = false;
connectError = fail;
};

this.clearMocks = () => {
mock_collections = {};
connected = false;
};

this.connect = () => {
return new Promise((resolve, reject) => {
process.nextTick(
() => {
if (connectError)
reject(new Error("Failed to connect"));
else {
resolve(true);
this.connected = true;
}
}
);
});
};

this.isConnected = () => connected;

this.connection = {
collection: (name) => {
mock_collections[name] = mock_collections[name] || {
__collection: name,
insert: jest.fn().mockImplementation((data) => {
const ObjectID = require.requireActual('mongodb').ObjectID;

let new_data = Object.assign({}, {
_id: new ObjectID()
},data);
return new Promise((resolve, reject) => {
process.nextTick(
() =>
resolve(new_data))
}
);
})
,
update: jest.fn(),
insertOne: jest.fn(),
updateOne: jest.fn(),
};
return mock_collections[name];
}
}
}

}

现在很少解释

  • jest.mock('mongodb');将确保任何实际的 mongodb 调用都被模拟
  • connected , connectError , mock_collections是全局变量。这样我们就可以影响 Db 的状态你的user.js负载。如果我们不这样做,我们将无法控制模拟的 Db。来 self 们的测试
  • this.connect展示了如何返回一个 promise ,以及如何在需要时模拟连接到数据库的错误
  • collection: (name) => {确保您调用 createUser并且您的测试可以获得相同的集合接口(interface)并检查是否实际调用了模拟函数。
  • insert: jest.fn().mockImplementation((data) => {展示如何通过创建自己的实现来返回数据
  • const ObjectID = require.requireActual('mongodb').ObjectID;展示了在模拟 mongodb 后如何获得实际的模块对象早些时候

现在是测试部分。这是更新的 user.test.js

jest.mock('../../lib/db');
import Db from '../../lib/db'
import { createUser } from '../../user'

const db = new Db()

describe('createUser()', () => {
beforeEach(()=> {db.clearMocks();})

test('should call mongoDB insert() and update() methods 2', async () => {
let User = db.connection.collection('users');
let user = await createUser({}, {
username: 'username',
password: 'password'
});
console.log(user);
expect(User.insert).toHaveBeenCalled()
})

test('Connection failure', async () => {
db.__connectError(true);
let ex = null;
try {
await createUser({}, {
username: 'username',
password: 'password'
})
} catch (err) {
ex= err;
}
expect(ex).not.toBeNull();
expect(ex.message).toBe("Failed to connect");
})
})

再次提出几点建议

  • jest.mock('../../lib/db');将确保加载我们的手动模拟
  • let user = await createUser({}, {因为你正在使用 async , 你不会使用 thencatch .这就是使用 async 的意义所在功能。
  • db.__connectError(true);将设置全局变量 connectedfalseconnectError为真。所以当createUser在测试中被调用它将模拟连接错误
  • ex= err; ,看看我是如何捕获异常并取出 expect 调用的。如果您确实希望在 catch block 本身,那么当没有引发异常时,测试仍然会通过。这就是为什么我在 try/catch 之外进行异常测试的原因阻止

现在是通过运行 npm test 对其进行测试的部分我们得到

Jest test results

所有这些都提交给您共享的以下 repo

https://github.com/jaqua/mongodb-class

关于javascript - 如何创建一个可模拟的类来连接到 mongoDB?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49345702/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com