如何在开玩笑地测试 Nexts 页面 api 时模拟 mongodb 客户端?

How to mock mongodb client when testing Nexts page api with jest?

我想用开玩笑测试下一页 api 文件,虽然目前测试 运行 没问题,但我遇到了一些问题

  1. 每个页面 API POST 发出的请求实际上是在向 mongo 数据库发送数据,即使我正在 运行 测试;这是不可取的,因为我不想在测试此类 POST 请求时将测试数据存储在数据库中
  2. 在某些情况下,我想模拟 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!',
      })
    )
  })

})