Javascript 如何连接到mongoDB并测试drop收集?
这就是我使用Javascript 如何连接到mongoDB并测试drop收集?,javascript,node.js,mongodb,unit-testing,jestjs,Javascript,Node.js,Mongodb,Unit Testing,Jestjs,这就是我使用monk()连接mongoDB的方式。我将把它存储在状态 假设我们想要删除一些集合,我们调用dropDB db.js var state = { db: null } export function connection () { if (state.db) return state.db = monk('mongdb://localhost:27017/db') return state.db } export async function dropDB ()
monk()
连接mongoDB的方式。我将把它存储在状态
假设我们想要删除一些集合,我们调用dropDB
db.js
var state = {
db: null
}
export function connection () {
if (state.db) return
state.db = monk('mongdb://localhost:27017/db')
return state.db
}
export async function dropDB () {
var db = state.db
if (!db) throw Error('Missing database connection')
const Users = db.get('users')
const Content = db.get('content')
await Users.remove({})
await Content.remove({})
}
import monk from 'monk'
import { connection, dropDB } from './db'
jest.mock('monk')
describe('dropDB()', () => {
test('should throw error if db connection is missing', async () => {
expect.assertions(1)
await expect(dropDB()).rejects.toEqual(Error('Missing database connection'))
})
})
export class Db {
constructor() {
this.connection = monk('mongdb://localhost:27017/db');
}
async dropDB() {
const Users = this.connection.get('users');
const Content = this.connection.get('content');
await Users.remove({});
await Content.remove({});
}
}
import { Db } from './db'
jest.mock('./db')
let db
let remove
describe('DB class', () => {
beforeAll(() => {
const remove = jest.fn(() => Promise.resolve({ n: 1, nRemoved: 1, ok: 1 }))
Db.mockImplementation(() => {
return { dropDB: () => {
// Define this.connection.get() and use remove as a result of it
} }
})
})
describe('dropDB()', () => {
test('should call remove method', () => {
db = new Db()
db.dropDB()
expect(remove).toHaveBeenCalledTimes(2)
})
})
})
我不太确定使用state
变量是否是一种好方法。也许有人可以对此发表评论或显示出改进
现在我想使用JestJS为这个函数编写一个单元测试:
db.test.js
var state = {
db: null
}
export function connection () {
if (state.db) return
state.db = monk('mongdb://localhost:27017/db')
return state.db
}
export async function dropDB () {
var db = state.db
if (!db) throw Error('Missing database connection')
const Users = db.get('users')
const Content = db.get('content')
await Users.remove({})
await Content.remove({})
}
import monk from 'monk'
import { connection, dropDB } from './db'
jest.mock('monk')
describe('dropDB()', () => {
test('should throw error if db connection is missing', async () => {
expect.assertions(1)
await expect(dropDB()).rejects.toEqual(Error('Missing database connection'))
})
})
export class Db {
constructor() {
this.connection = monk('mongdb://localhost:27017/db');
}
async dropDB() {
const Users = this.connection.get('users');
const Content = this.connection.get('content');
await Users.remove({});
await Content.remove({});
}
}
import { Db } from './db'
jest.mock('./db')
let db
let remove
describe('DB class', () => {
beforeAll(() => {
const remove = jest.fn(() => Promise.resolve({ n: 1, nRemoved: 1, ok: 1 }))
Db.mockImplementation(() => {
return { dropDB: () => {
// Define this.connection.get() and use remove as a result of it
} }
})
})
describe('dropDB()', () => {
test('should call remove method', () => {
db = new Db()
db.dropDB()
expect(remove).toHaveBeenCalledTimes(2)
})
})
})
这一部分很简单,但下一部分给了我两个问题:
如何模拟remove()
方法
test('should call remove() methods', async () => {
connection() // should set `state.db`, but doesn't work
const remove = jest.fn(() => Promise.resolve({ n: 1, nRemoved: 1, ok: 1 }))
// How do I use this mocked remove()?
expect(remove).toHaveBeenCalledTimes(2)
})
在那之前呢?如何设置state.db
更新 正如poke所解释的,全局变量产生了问题。所以我换了一门课: db.js
var state = {
db: null
}
export function connection () {
if (state.db) return
state.db = monk('mongdb://localhost:27017/db')
return state.db
}
export async function dropDB () {
var db = state.db
if (!db) throw Error('Missing database connection')
const Users = db.get('users')
const Content = db.get('content')
await Users.remove({})
await Content.remove({})
}
import monk from 'monk'
import { connection, dropDB } from './db'
jest.mock('monk')
describe('dropDB()', () => {
test('should throw error if db connection is missing', async () => {
expect.assertions(1)
await expect(dropDB()).rejects.toEqual(Error('Missing database connection'))
})
})
export class Db {
constructor() {
this.connection = monk('mongdb://localhost:27017/db');
}
async dropDB() {
const Users = this.connection.get('users');
const Content = this.connection.get('content');
await Users.remove({});
await Content.remove({});
}
}
import { Db } from './db'
jest.mock('./db')
let db
let remove
describe('DB class', () => {
beforeAll(() => {
const remove = jest.fn(() => Promise.resolve({ n: 1, nRemoved: 1, ok: 1 }))
Db.mockImplementation(() => {
return { dropDB: () => {
// Define this.connection.get() and use remove as a result of it
} }
})
})
describe('dropDB()', () => {
test('should call remove method', () => {
db = new Db()
db.dropDB()
expect(remove).toHaveBeenCalledTimes(2)
})
})
})
这将导致此测试文件:
db.test.js
var state = {
db: null
}
export function connection () {
if (state.db) return
state.db = monk('mongdb://localhost:27017/db')
return state.db
}
export async function dropDB () {
var db = state.db
if (!db) throw Error('Missing database connection')
const Users = db.get('users')
const Content = db.get('content')
await Users.remove({})
await Content.remove({})
}
import monk from 'monk'
import { connection, dropDB } from './db'
jest.mock('monk')
describe('dropDB()', () => {
test('should throw error if db connection is missing', async () => {
expect.assertions(1)
await expect(dropDB()).rejects.toEqual(Error('Missing database connection'))
})
})
export class Db {
constructor() {
this.connection = monk('mongdb://localhost:27017/db');
}
async dropDB() {
const Users = this.connection.get('users');
const Content = this.connection.get('content');
await Users.remove({});
await Content.remove({});
}
}
import { Db } from './db'
jest.mock('./db')
let db
let remove
describe('DB class', () => {
beforeAll(() => {
const remove = jest.fn(() => Promise.resolve({ n: 1, nRemoved: 1, ok: 1 }))
Db.mockImplementation(() => {
return { dropDB: () => {
// Define this.connection.get() and use remove as a result of it
} }
})
})
describe('dropDB()', () => {
test('should call remove method', () => {
db = new Db()
db.dropDB()
expect(remove).toHaveBeenCalledTimes(2)
})
})
})
如何模拟任何
此元素?在这种情况下,我需要模拟这个.connection.get()
全局状态肯定是问题的根源。我建议寻找一种根本不涉及全局变量的解决方案。根据,全局变量会导致紧密耦合,并使测试变得困难(正如您自己所注意到的)
更好的解决方案是,将数据库连接显式地传递给dropDB
函数,使其将连接作为显式依赖项,或者引入一些有状态对象来保持连接并将dropDB
作为方法提供
第一个选项如下所示:
export function openConnection() {
return monk('mongdb://localhost:27017/db');
}
export async function dropDB(connection) {
if (!connection) {
throw Error('Missing database connection');
}
const Users = connection.get('users');
const Content = connection.get('content');
await Users.remove({});
await Content.remove({});
}
export class Connection() {
constructor() {
this.connection = monk('mongdb://localhost:27017/db');
}
async dropDB() {
const Users = this.connection.get('users');
const Content = this.connection.get('content');
await Users.remove({});
await Content.remove({});
}
}
test('should call remove() methods', async () => {
const usersRemove = jest.fn().mockReturnValue(Promise.resolve(null));
const contentRemove = jest.fn().mockReturnValue(Promise.resolve(null));
const dbMock = {
get(type) {
if (type === 'users') {
return { remove: usersRemove };
}
else if (type === 'content') {
return { remove: contentRemove };
}
}
};
await dropDB(dbMock);
expect(usersRemove).toHaveBeenCalledTimes(1);
expect(contentRemove).toHaveBeenCalledTimes(1);
});
这也使得测试dropDB变得非常容易,因为您现在可以直接为它传递一个模拟对象
另一个选项可能如下所示:
export function openConnection() {
return monk('mongdb://localhost:27017/db');
}
export async function dropDB(connection) {
if (!connection) {
throw Error('Missing database connection');
}
const Users = connection.get('users');
const Content = connection.get('content');
await Users.remove({});
await Content.remove({});
}
export class Connection() {
constructor() {
this.connection = monk('mongdb://localhost:27017/db');
}
async dropDB() {
const Users = this.connection.get('users');
const Content = this.connection.get('content');
await Users.remove({});
await Content.remove({});
}
}
test('should call remove() methods', async () => {
const usersRemove = jest.fn().mockReturnValue(Promise.resolve(null));
const contentRemove = jest.fn().mockReturnValue(Promise.resolve(null));
const dbMock = {
get(type) {
if (type === 'users') {
return { remove: usersRemove };
}
else if (type === 'content') {
return { remove: contentRemove };
}
}
};
await dropDB(dbMock);
expect(usersRemove).toHaveBeenCalledTimes(1);
expect(contentRemove).toHaveBeenCalledTimes(1);
});
第一个选项的测试可能如下所示:
export function openConnection() {
return monk('mongdb://localhost:27017/db');
}
export async function dropDB(connection) {
if (!connection) {
throw Error('Missing database connection');
}
const Users = connection.get('users');
const Content = connection.get('content');
await Users.remove({});
await Content.remove({});
}
export class Connection() {
constructor() {
this.connection = monk('mongdb://localhost:27017/db');
}
async dropDB() {
const Users = this.connection.get('users');
const Content = this.connection.get('content');
await Users.remove({});
await Content.remove({});
}
}
test('should call remove() methods', async () => {
const usersRemove = jest.fn().mockReturnValue(Promise.resolve(null));
const contentRemove = jest.fn().mockReturnValue(Promise.resolve(null));
const dbMock = {
get(type) {
if (type === 'users') {
return { remove: usersRemove };
}
else if (type === 'content') {
return { remove: contentRemove };
}
}
};
await dropDB(dbMock);
expect(usersRemove).toHaveBeenCalledTimes(1);
expect(contentRemove).toHaveBeenCalledTimes(1);
});
基本上,dropDB
函数需要具有get
方法的对象,调用该方法时返回具有remove
方法的对象。因此,您只需要传递这样的内容,这样函数就可以调用那些remove
方法
对于类来说,这有点复杂,因为构造函数依赖于monk
模块。一种方法是再次明确该依赖关系(就像在第一个解决方案中一样),并在那里传递monk
或其他工厂。但是我们也可以使用它来简单地模拟整个monk
模块
请注意,我们不想模拟包含连接的模块。我们想测试它,所以我们需要它处于非模拟状态
要模拟monk
,我们需要在\uuuumocks\uuuuu/monk.js
上创建它的模拟模块。手册指出,该\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
文件夹应与节点模块
文件夹相邻
在该文件中,我们只需导出自定义monk
函数。这与我们在第一个示例中已经使用的方法基本相同,因为我们只关心将那些remove
方法放在适当的位置:
export default function mockedMonk (url) {
return {
get(type) {
if (type === 'users') {
return { remove: mockedMonk.usersRemove };
}
else if (type === 'content') {
return { remove: mockedMonk.contentRemove };
}
}
};
};
请注意,这是指函数mockedMonk.usersRemove
和mockedMonk.contentRemove
。我们将在测试中使用它来在测试执行期间显式配置这些函数
现在,在测试函数中,我们需要调用jest.mock('monk')
,以使jest能够用模拟模块模拟monk
模块。然后,我们也可以导入它并在测试中设置我们的函数。基本上,如上所述:
import { Connection } from './db';
import monk from 'monk';
// enable mock
jest.mock('./monk');
test('should call remove() methods', async () => {
monk.usersRemove = jest.fn().mockReturnValue(Promise.resolve(null));
monk.contentRemove = jest.fn().mockReturnValue(Promise.resolve(null));
const connection = new Connection();
await connection.dropDB();
expect(monk.usersRemove).toHaveBeenCalledTimes(1);
expect(monk.contentRemove).toHaveBeenCalledTimes(1);
});
你在打转接电话吗?我没有看到对“connect”的调用。我已经为第一个解决方案添加了一个示例测试。我以前没有真正使用过Jest,所以我不知道这是否是最好的方法。我已经更新了帖子,因为我决定使用类切换到您的解决方案。但是我一直在模仿任何这个实例。你能告诉我怎么弄到这个吗?我可以完全模仿它吗?或者我必须通过模仿monk()
,间接地模仿它吗?@user3142695为基于类的解决方案更新了一个工作测试。非常感谢您的精彩解释。这对我了解更多细节帮助很大。