use*_*695 7 javascript unit-testing node.js jestjs
我试图创建一个连接到mongoDB的类(并使用(gridfs-stream)获取gridFS连接.但是我确实遇到了两个问题:
server instance in invalid state connected所以如果有人能帮助我优化这门课程以获得一个非常扎实的工人阶级,我将非常感激.例如,我不喜欢let that = thisconnect()函数.
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
}
}
Run Code Online (Sandbox Code Playgroud)
例
此函数将使用上面的类向DB添加新用户:
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()
})
}
Run Code Online (Sandbox Code Playgroud)
单元测试
我需要创建一个单元测试来测试是否调用了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
})
})
})
Run Code Online (Sandbox Code Playgroud)
有多种方法可以解决这个问题。我将列出其中的一些
我将在这里展示第一个案例,您发布了该案例的代码以及如何使其工作。所以我们要做的第一件事就是将__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];
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在很少解释
jest.mock('mongodb');将确保任何实际的 mongodb 调用都会被嘲笑connected、connectError是mock_collections全局变量。这样我们就可以影响Db您user.js加载的状态。如果我们不这样做,我们将无法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");
})
})
Run Code Online (Sandbox Code Playgroud)
再次指点几句
jest.mock('../../lib/db');将确保我们的手动模拟被加载let user = await createUser({}, {既然你正在使用async,你就不会使用then或catch。这就是使用函数的要点async。db.__connectError(true);会将全局变量设置connected为false和connectErrortrue。因此,当createUser在测试中被调用时,它将模拟连接错误ex= err;,看看我如何捕获异常并取出 Expect 调用。如果您确实期望在catch块本身中,那么当没有引发异常时,测试仍然会通过。try/catch这就是为什么我在块外进行异常测试的原因现在是通过运行来测试它的部分npm test,我们得到
所有这些都致力于您共享的以下存储库
https://github.com/jaqua/mongodb-class