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
- 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
- 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!',
})
)
})
})
