如何创建一个可模拟的类来连接到mongoDB?
我尝试创建一个类来连接到mongoDB(并使用( gridfs-stream
)获得一个gridFS连接。但是,我得到两个问题:
server instance in invalid state connected
的mongo错误server instance in invalid state connected
所以,如果有人能够帮助我优化这个班级以获得一个非常扎实的工作课程,我将非常感激。 例如,我不喜欢connect()函数中的let that = this
。
示例回购
DB类
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
})
})
})
有很多方法可以解决这个问题。 我将列出其中的几个
我将在这里展示第一个案例,这是你使用代码发布的,以及如何使其工作。 所以我们要做的第一件事就是将__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
是全局变量。 这样可以影响你的user.js
加载的Db
的状态。 如果我们不这样做,我们将无法在测试中控制嘲笑的Db
this.connect
显示了如何返回承诺,以及如何在需要时模拟连接到数据库的错误 collection: (name) => {
确保您对createUser
和您的测试的调用可以获得相同的集合接口,并检查是否实际调用了模拟函数。 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
,你不会用then
或catch
,这是使用的点async
功能。 db.__connectError(true);
将设置全局变量connected
到false
并connectError
为true。 因此,当createUser
在测试中被调用时,它将模拟连接错误 ex= err;
,看看我如何捕捉异常并取消预期的呼叫。 如果你确实期望在catch
块本身,那么当没有引发异常时,测试仍然会通过。 这就是为什么我在try/catch
块之外进行异常测试的原因 现在通过运行npm test
来测试它,我们得到了
所有这一切都致力于低于你分享的回购
https://github.com/jaqua/mongodb-class
您正在存储一个DB实例,而不是实际的DB类。 另外,我在代码db.usersInsert
不到db.usersInsert
方法。 我们不能为您编写代码,但我可以指出您正确的方向。 另外,我不使用Jest,但Sinon的概念是一样的。 在你的情况下,我相信最好的办法是将类方法的原型存根出来,以返回你正在与之交互的对象。
像这样的东西:
// db.js
export default class Db {
getConnection() {}
}
// someOtherFile.js
import Db from './db';
const db = new Db();
export default async () => {
const conn = await db.getConnection();
await connection.collection.insert();
}
// spec file
import {
expect
} from 'chai';
import {
set
} from 'lodash';
import sinon from 'sinon';
import Db from './db';
import testFn from './someOtherFile';
describe('someOtherFile', () => {
it('should call expected funcs from db class', async () => {
const spy = sinon.spy();
const stub = sinon.stub(Db.prototype, 'getConnection').callsFake(() => {
return set({}, 'collection.insert', spy);
});
await testFn();
sinon.assert.called(spy);
});
});
链接地址: http://www.djcxy.com/p/41439.html