use*_*695 1 javascript unit-testing jestjs
在我的应用程序代码中有几个地方,我必须连接到数据库并获取一些数据。对于我的单元测试(我正在使用 JestJS),我需要对此进行模拟。
让我们假设这个简单的异步函数:
/getData.js
import DB from './lib/db'
export async function getData () {
const db = DB.getDB()
const Content = db.get('content')
const doc = await Content.findOne({ _id: id })
return doc
}
Run Code Online (Sandbox Code Playgroud)
数据库连接在一个单独的文件中:
/lib/db.js
import monk from 'monk'
var state = {
db: null
}
exports.connect = (options, done) => {
if (state.db) return done()
state.db = monk(
'mongodb://localhost:27017/db',
options
)
return state.db
}
exports.getDB = () => {
return state.db
}
Run Code Online (Sandbox Code Playgroud)
你可以看到,我会收到数据库并收集。在此之后,我将收到数据。
到目前为止,我对模拟的尝试:
/tests/getData.test.js
import { getData } from '../getData'
import DB from './lib/db'
describe('getData()', () => {
beforeEach(() => {
DB.getDB = jest.fn()
.mockImplementation(
() => ({
get: jest.fn(
() => ({
findOne: jest.fn(() => null)
})
)
})
)
})
test('should return null', () => {
const result = getData()
expect(result).toBeNull()
})
})
Run Code Online (Sandbox Code Playgroud)
也许这不是最好的方法......?我对每一次改进都感到非常高兴。
我的问题是将 DB 模拟放在哪里,因为有多个测试,每个测试都需要不同的findOne()调用模拟结果。
也许可以创建一个函数,该函数使用所需的参数或类似的参数进行调用。
首先,我只想指出,按原样测试这个概念验证功能的价值似乎很低。那里没有你的任何代码;这都是对数据库客户端的调用。该测试基本上是在验证,如果您模拟 DB 客户端返回 null,则它返回 null。所以你真的只是在测试你的模拟。
但是,如果您的函数在返回数据之前以某种方式转换了数据,那将会很有用。(尽管在这种情况下,我会将变换放在自己的函数中并进行自己的测试,让我们回到开始的地方。)
因此,我会建议一个解决方案来满足您的要求,然后是一个有望改进您的代码的解决方案。
getData()- 不推荐:您可以创建一个返回模拟的函数,该模拟提供findOne()返回您指定的任何内容的模拟:
// ./db-test-utils
function makeMockGetDbWithFindOneThatReturns(returnValue) {
const findOne = jest.fn(() => Promise.resolve(returnValue));
return jest.fn(() => ({
get: () => ({ findOne })
}));
}
Run Code Online (Sandbox Code Playgroud)
然后在您的代码文件中,DB.getDB.mockImplementation在每个测试上方调用beforeEach 或 beforeAll,传入所需的返回值,如下所示:
import DB from './db';
jest.mock('./db');
describe('testing getThingById()', () => {
beforeAll(() => {
DB.getDB.mockImplementation(makeMockGetDbWithFindOneThatReturns(null));
});
test('should return null', async () => {
const result = await getData();
expect(result).toBeNull();
});
});
Run Code Online (Sandbox Code Playgroud)
这个问题真的很令人兴奋,因为它很好地说明了让每个函数只做一件事的价值!
getData看起来很小——只有 3 行加上一条return语句。所以乍一看它似乎并没有做太多。
然而,这个微小的函数与 的内部结构有着非常紧密的耦合DB。它依赖于:
DB - 单身人士DB.getDB()DB.getDB().get()DB.getDB().get().findOne()这有一些负面影响:
DB更改其结构(因为它使用 3rd 方组件)是可能的,那么您拥有的每个具有这些依赖项的功能都会中断。getDB()和db.get('collection'),导致代码重复。这是您可以改进事物的一种方法,同时使您的测试模拟更加简单。
db而不是DB我可能是错的,但我的猜测是,每次使用 时DB,您要做的第一件事就是调用getDB()。但是您只需要在整个代码库中进行一次该调用。您可以db从而./lib/db.js不是在任何地方重复该代码,而不是DB:
// ./lib/db.js
const DB = existingCode(); // However you're creating DB now
const dbInstance = DB.getDB();
export default dbInstance;
Run Code Online (Sandbox Code Playgroud)
或者,您可以在启动函数中创建 db 实例,然后将其传递给 DataAccessLayer 类,该类将容纳您的所有数据库访问调用。再次只调用getDB()一次。这样你就避免了单例,这使得测试更容易,因为它允许依赖注入。
// ./lib/db.js
const DB = existingCode(); // However you're creating DB now
const dbInstance = DB.getDB();
export function getCollectionByName(collectionName){
return dbInstance.get(collectionName);
}
export default dbInstance;
Run Code Online (Sandbox Code Playgroud)
这个函数非常微不足道,似乎没有必要。毕竟,它的行数与其替换的代码行数相同!但是它从调用代码中消除了对dbInstance(以前的 db)结构的依赖,同时记录了get()它的作用(这从它的名字中看不出来)。
现在你的getData,我正在重命名getDocById以反映它的实际作用,它看起来像这样:
import { getCollectionByName } from './lib/db';
export async function getDocById(id) {
const collection = getCollectionByName('things');
const doc = await collection.findOne({ _id: id })
return doc;
}
Run Code Online (Sandbox Code Playgroud)
现在您可以将 getCollectionByName 与 DB 分开模拟:
// getData.test.js
import { getDocById } from '../getData'
import { getCollectionByName } from './lib/db'
jest.mock('./lib/db');
describe('testing getThingById()', () => {
beforeEach(() => {
getCollectionByName.mockImplementation(() => ({
findOne: jest.fn(() => Promise.resolve(null))
}));
});
test('should return null', async () => {
const result = await getDocById();
expect(result).toBeNull();
});
});
Run Code Online (Sandbox Code Playgroud)
这只是一种方法,可以采取更进一步的方法。例如,我们可以导出findOneDocById(collectionName, id)和/或findOneDoc(collectionName, searchObject)使我们的模拟和调用findOne()更简单。
| 归档时间: |
|
| 查看次数: |
3894 次 |
| 最近记录: |