如何在开玩笑地测试 Nexts 页面 api 时模拟 mongodb 客户端?
How to mock mongodb client when testing Nexts page api with jest?
我想用开玩笑测试下一页 api 文件,虽然目前测试 运行 没问题,但我遇到了一些问题
- 每个页面 API POST 发出的请求实际上是在向 mongo 数据库发送数据,即使我正在 运行 测试;这是不可取的,因为我不想在测试此类 POST 请求时将测试数据存储在数据库中
- 在某些情况下,我想模拟 mongo 连接失败,以测试 500 状态错误响应,但我不确定该怎么做
我如何模拟 mongo 客户端连接,以便在成功连接后测试将项目插入数据库,或者故意导致 mongo 客户端连接错误?
newsletter.ts (PAGE API FILE)
import type { NextApiRequest, NextApiResponse } from 'next'
import { MongoClient } from 'mongodb'
async function connectDatabase() {
const client = await MongoClient.connect(<url>)
return client
}
async function insertDocument(client: MongoClient, document: { email: string }) {
const db = client.db()
await db.collection('newsletter').insertOne(document)
}
async function handler(req: NextApiRequest, res: NextApiResponse) {
const userEmail = JSON.parse(req.body).email
if (!userEmail || !userEmail.includes('@')) {
res.status(422).json({ message: 'Invalid email address' })
return
}
let client
try {
client = await connectDatabase()
} catch (error) {
res.status(500).json({ message: 'Connecting to the database failed!' })
return
}
try {
await insertDocument(client, { email: userEmail })
} catch(error) {
res.status(500).json({ message: 'Inserting data failed!' })
return
}
client.close()
res.status(201).json({ message: 'Signed up!' })
}
export default handler
这是测试代码
newsletter.test.tsx (PAGE API TEST FILE)
import type { NextApiRequest, NextApiResponse } from 'next'
import {
createMocks as _createMocks,
Mocks,
RequestOptions,
ResponseOptions,
} from 'node-mocks-http'
import newsletter from '../../pages/api/newsletter'
const createMocks = _createMocks as (
reqOptions?: RequestOptions,
resOptions?: ResponseOptions
) => Mocks<NextApiRequest, NextApiResponse>
describe('Test responses for newsletter api', () => {
it('Should return status 201 with valid request body', async () => {
const { req, res } = createMocks({
method: 'POST',
body: JSON.stringify({
email: 'abc@email.com',
}),
})
await newsletter(req, res)
expect(res._getStatusCode()).toBe(201)
expect(JSON.parse(res._getData())) .toEqual(
expect.objectContaining({
message: 'Signed up!',
})
)
})
it('Should return status 422 with invalid req body', async () => {
const { req, res } = createMocks({
method: 'POST',
body: JSON.stringify({
email: 'abcemail.com',
}),
})
await newsletter(req, res)
expect(res._getStatusCode()).toBe(422)
expect(JSON.parse(res._getData())).toEqual(
expect.objectContaining({
message: 'Invalid email address',
})
)
})
})
找到这个 帮助我解决了问题
为了模拟 mongoclient,我只需要使用 jest.spyOn,并链接用户将文档插入数据库的所有其他函数。这也阻止了对服务器的真正调用,并且不再发送测试数据。为了模拟状态 500 错误,我只是模拟了一个被拒绝的承诺。
代码最终变成了这样:
import type { NextApiRequest, NextApiResponse } from 'next'
import {
createMocks as _createMocks,
Mocks,
RequestOptions,
ResponseOptions,
} from 'node-mocks-http'
import { MongoClient } from 'mongodb'
import newsletter from '../../pages/api/newsletter'
const createMocks = _createMocks as (
reqOptions?: RequestOptions,
resOptions?: ResponseOptions
) => Mocks<NextApiRequest, NextApiResponse>
describe('Test responses for newsletter api', () => {
afterEach(() => {
jest.resetAllMocks()
})
it('Should return status 500 on failure to connect to database', async () => {
const { req, res } = createMocks({
method: 'POST',
//@ts-ignore
body: JSON.stringify({
email: 'abc@email.com',
}),
})
const connectSpy = jest.spyOn(MongoClient, 'connect').mockRejectedValueOnce(new Error)
await newsletter(req, res)
expect(connectSpy).toHaveBeenCalled()
expect(res._getStatusCode()).toBe(500)
})
it('Should return status 201 with valid request body', async () => {
const { req, res } = createMocks({
method: 'POST',
//@ts-ignore
body: JSON.stringify({
email: 'abc@email.com',
}),
})
const insertOne = jest.fn().mockResolvedValueOnce({ acknowleged: true })
const collection = jest.fn().mockReturnValueOnce({ insertOne })
const connectSpy = jest.spyOn(MongoClient, 'connect').mockResolvedValueOnce({ db: jest.fn().mockReturnValueOnce({ collection }), close: jest.fn() })
await newsletter(req, res)
expect(connectSpy).toHaveBeenCalled()
expect(res._getStatusCode()).toBe(201)
expect(JSON.parse(res._getData())) .toEqual(
expect.objectContaining({
message: 'Signed up!',
})
)
})
})
我想用开玩笑测试下一页 api 文件,虽然目前测试 运行 没问题,但我遇到了一些问题
- 每个页面 API POST 发出的请求实际上是在向 mongo 数据库发送数据,即使我正在 运行 测试;这是不可取的,因为我不想在测试此类 POST 请求时将测试数据存储在数据库中
- 在某些情况下,我想模拟 mongo 连接失败,以测试 500 状态错误响应,但我不确定该怎么做
我如何模拟 mongo 客户端连接,以便在成功连接后测试将项目插入数据库,或者故意导致 mongo 客户端连接错误?
newsletter.ts (PAGE API FILE)
import type { NextApiRequest, NextApiResponse } from 'next'
import { MongoClient } from 'mongodb'
async function connectDatabase() {
const client = await MongoClient.connect(<url>)
return client
}
async function insertDocument(client: MongoClient, document: { email: string }) {
const db = client.db()
await db.collection('newsletter').insertOne(document)
}
async function handler(req: NextApiRequest, res: NextApiResponse) {
const userEmail = JSON.parse(req.body).email
if (!userEmail || !userEmail.includes('@')) {
res.status(422).json({ message: 'Invalid email address' })
return
}
let client
try {
client = await connectDatabase()
} catch (error) {
res.status(500).json({ message: 'Connecting to the database failed!' })
return
}
try {
await insertDocument(client, { email: userEmail })
} catch(error) {
res.status(500).json({ message: 'Inserting data failed!' })
return
}
client.close()
res.status(201).json({ message: 'Signed up!' })
}
export default handler
这是测试代码
newsletter.test.tsx (PAGE API TEST FILE)
import type { NextApiRequest, NextApiResponse } from 'next'
import {
createMocks as _createMocks,
Mocks,
RequestOptions,
ResponseOptions,
} from 'node-mocks-http'
import newsletter from '../../pages/api/newsletter'
const createMocks = _createMocks as (
reqOptions?: RequestOptions,
resOptions?: ResponseOptions
) => Mocks<NextApiRequest, NextApiResponse>
describe('Test responses for newsletter api', () => {
it('Should return status 201 with valid request body', async () => {
const { req, res } = createMocks({
method: 'POST',
body: JSON.stringify({
email: 'abc@email.com',
}),
})
await newsletter(req, res)
expect(res._getStatusCode()).toBe(201)
expect(JSON.parse(res._getData())) .toEqual(
expect.objectContaining({
message: 'Signed up!',
})
)
})
it('Should return status 422 with invalid req body', async () => {
const { req, res } = createMocks({
method: 'POST',
body: JSON.stringify({
email: 'abcemail.com',
}),
})
await newsletter(req, res)
expect(res._getStatusCode()).toBe(422)
expect(JSON.parse(res._getData())).toEqual(
expect.objectContaining({
message: 'Invalid email address',
})
)
})
})
找到这个
为了模拟 mongoclient,我只需要使用 jest.spyOn,并链接用户将文档插入数据库的所有其他函数。这也阻止了对服务器的真正调用,并且不再发送测试数据。为了模拟状态 500 错误,我只是模拟了一个被拒绝的承诺。
代码最终变成了这样:
import type { NextApiRequest, NextApiResponse } from 'next'
import {
createMocks as _createMocks,
Mocks,
RequestOptions,
ResponseOptions,
} from 'node-mocks-http'
import { MongoClient } from 'mongodb'
import newsletter from '../../pages/api/newsletter'
const createMocks = _createMocks as (
reqOptions?: RequestOptions,
resOptions?: ResponseOptions
) => Mocks<NextApiRequest, NextApiResponse>
describe('Test responses for newsletter api', () => {
afterEach(() => {
jest.resetAllMocks()
})
it('Should return status 500 on failure to connect to database', async () => {
const { req, res } = createMocks({
method: 'POST',
//@ts-ignore
body: JSON.stringify({
email: 'abc@email.com',
}),
})
const connectSpy = jest.spyOn(MongoClient, 'connect').mockRejectedValueOnce(new Error)
await newsletter(req, res)
expect(connectSpy).toHaveBeenCalled()
expect(res._getStatusCode()).toBe(500)
})
it('Should return status 201 with valid request body', async () => {
const { req, res } = createMocks({
method: 'POST',
//@ts-ignore
body: JSON.stringify({
email: 'abc@email.com',
}),
})
const insertOne = jest.fn().mockResolvedValueOnce({ acknowleged: true })
const collection = jest.fn().mockReturnValueOnce({ insertOne })
const connectSpy = jest.spyOn(MongoClient, 'connect').mockResolvedValueOnce({ db: jest.fn().mockReturnValueOnce({ collection }), close: jest.fn() })
await newsletter(req, res)
expect(connectSpy).toHaveBeenCalled()
expect(res._getStatusCode()).toBe(201)
expect(JSON.parse(res._getData())) .toEqual(
expect.objectContaining({
message: 'Signed up!',
})
)
})
})