Testing the Middleware

Recall how the user registration endpoint did not have any middleware. For the purposes of demonstrating how to test a middleware, we'll just be grabbing the isAuthenticated middleware that we created and test that instead.

File: src/middleware/auth.middleware.js

const globalResponseDto = require('../responses/globalResponseDto')

const isAuthenticated = (req, res, next) => {
  if (!req.session.user) {
    return res.status(401).json(
      globalResponseDto({
        status: 'error',
        code: 401,
        message:
          'Access denied: you must be logged in to access this API endpoint.',
        data: null,
        errors: ['You must be logged in.']
      })
    )
  }

  next()
}

module.exports = isAuthenticated

Testing the middleware is a lot trickier than just a regular function. This reason why is because we are expected to stick to a particular function signature (req, res, next).

The answer to this is to mock out those particular parameters. The way to do it is to use jest.fn, which we will use to help us write out helpers like mockRequest, mockResponse, and mockNext functions.

File: src/middleware/__tests__/auth.middleware.test.js

const isAuthenticated = require('../auth.middleware')
const globalResponseDto = require('../../responses/globalResponseDto')

const mockRequest = (userData) => ({
  session: {
    user: userData
  }
})

const mockResponse = () => {
  const res = {}

  res.status = jest.fn().mockReturnValue(res)
  res.json = jest.fn().mockReturnValue(res)

  return res
}

const mockNext = () => {
  return jest.fn()
}

describe('Test Suite: isAuthenticated middleware', () => {
  // our tests go here...
})

The Passing Test

Now watch as we pass the mockRequest, mockResponse, and mockNext functions into our isAuthenticated function. If everything goes well, then we should expect the next to be invoked.

test('Access granted, next() should be invoked in express', async () => {
  // 1. Arrange
  const req = mockRequest({ first_name: 'john' })
  const res = mockResponse()
  const next = mockNext()

  // 2. Act
  await isAuthenticated(req, res, next)

  // 3. Assert
  expect(next).toHaveBeenCalled()
})

The Passing Test

Now for the failing test, we should expect the correct status code and output.

test('Access denied, respond with a status 401', async () => {
  // 1. Arrange
  const req = mockRequest()
  const res = mockResponse()

  // 2. Act
  await isAuthenticated(req, res)
  
  // 3. Assert
  expect(res.status).toHaveBeenCalledWith(401)
  expect(res.json).toHaveBeenCalledWith(
    globalResponseDto({
      status: 'error',
      code: 401,
      message:
        'Access denied: you must be logged in to access this API endpoint.',
      data: null,
      errors: ['You must be logged in.']
    })
  )
})

Last updated