Home > Software engineering >  How to mock mongodb client when testing Nexts page api with jest?
How to mock mongodb client when testing Nexts page api with jest?

Time:02-01

I want to test a page next page api file with jest, and although currently the tests are running fine, I am having some issues

  1. Every Page API POST request that is being made is actually sending data to the mongo database, even when I am running the tests; this is not desirable, since I dont want test data to be on the database when I am testing such POST requests
  2. There are some cases where I want to simulate a failure on mongo connection, to test the 500 status error responses, but I am not sure how to do this

How can I mock the mongoclient connection in a way that I can either test the insertion of items into the database after a successful connection, or purposely cause a mongo client connection error?

    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

And here is the TEST code

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: '[email protected]',
      }),
    })
    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',
      })
    )
  })
})

CodePudding user response:

Found this answer which helped me to solve the issue

To mock the mongoclient, I just had to use jest.spyOn, and chain all the other functions that are user to insert a doocument into the db. This also prevented a real call to the server, and the test data was not being sent anymore. To mock the status 500 error, I just had too mock a rejected promise.

The code ended up becoming this:

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: '[email protected]',
      }),
    })
    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: '[email protected]',
      }),
    })
    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!',
      })
    )
  })

})
  •  Tags:  
  • Related