surferwat
surferwat

Reputation: 117

How to write a unit test an Express controller using Jest?

Thank you for taking the time to read my question.

I am pretty new to Jest and unit testing for that matter, so before I begin writing unit tests for my controller functions, I wanted to check my understanding. So high level, I understand that the unit test is to test whether the controller function behaves as expected when called.

How would I go about writing a Jest unit test for the following code? I would really appreciate an example so that I can reference it as I write the jest tests for the other controllers. Thank you.

//retrieveMember.js


const debug = require('debug')('get_member');
const MemberService = require('../services/MemberService');
const generateError = require('../lib/generateError');


const retrieveMember = async (req, res, next) => {
  try {
    if (req.params.id === ""
    || req.params.id === undefined) {
      throw generateError(400, "invalid.");
    }

    const regex = RegExp("[^a-zA-Z0-9\-]");
    if (regex.test(req.params.id)) {
      throw generateError(400, "invalid format.");
    }

    const memberId = req.params.id;
    const memberRecord = await MemberService.retrieveOneMember(memberId);

    const member_detail = {
      id: memberRecord.id,
      username: memberRecord.username,
    };

    res.status(200).send({member_detail: member_detail});
  } catch (e) {
    if (e) {
      debug('could not get member');
      return next(e);
    }
  }
};


module.exports = retrieveMember;

Upvotes: 6

Views: 16291

Answers (1)

Lin Du
Lin Du

Reputation: 102597

Here is the unit test solution:

retrieveMember.js:

const debug = console.debug;
const MemberService = require('./MemberService');
const generateError = require('./generateError');

const retrieveMember = async (req, res, next) => {
  try {
    if (req.params.id === '' || req.params.id === undefined) {
      throw generateError(400, 'invalid.');
    }

    const regex = RegExp('[^a-zA-Z0-9-]');
    if (regex.test(req.params.id)) {
      throw generateError(400, 'invalid format.');
    }

    const memberId = req.params.id;
    const memberRecord = await MemberService.retrieveOneMember(memberId);

    const member_detail = {
      id: memberRecord.id,
      username: memberRecord.username,
    };

    res.status(200).send({ member_detail: member_detail });
  } catch (e) {
    if (e) {
      debug('could not get member');
      return next(e);
    }
  }
};

module.exports = retrieveMember;

MemberService.js:

const MemberService = {
  async retrieveOneMember() {
    return 'real data';
  },
};

module.exports = MemberService;

generateError.js:

function generateError(status, message) {
  return new Error(message);
}

module.exports = generateError;

retrieveMember.test.js:

const retrieveMember = require('./retrieveMember');
const MemberService = require('./MemberService');

describe('61834610', () => {
  it('should throw 400 error if id is empty string', async () => {
    const mReq = { params: { id: '' } };
    const mRes = {};
    const mNext = jest.fn();
    await retrieveMember(mReq, mRes, mNext);
    expect(mNext).toBeCalledWith(new Error('invalid.'));
  });

  it('should throw 400 error if id is undefined', async () => {
    const mReq = { params: {} };
    const mRes = {};
    const mNext = jest.fn();
    await retrieveMember(mReq, mRes, mNext);
    expect(mNext).toBeCalledWith(new Error('invalid.'));
  });

  it('should throw 400 error if id is invalid format', async () => {
    const mReq = { params: { id: '$$' } };
    const mRes = {};
    const mNext = jest.fn();
    await retrieveMember(mReq, mRes, mNext);
    expect(mNext).toBeCalledWith(new Error('invalid format.'));
  });

  it('should retrieve one member by id and send response correctly', async () => {
    const mMemberRecord = { id: '1', username: 'KF1' };
    jest.spyOn(MemberService, 'retrieveOneMember').mockResolvedValueOnce(mMemberRecord);
    const mReq = { params: { id: '1' } };
    const mRes = { status: jest.fn().mockReturnThis(), send: jest.fn() };
    const mNext = jest.fn();
    await retrieveMember(mReq, mRes, mNext);
    expect(MemberService.retrieveOneMember).toBeCalledWith('1');
    expect(mRes.status).toBeCalledWith(200);
    expect(mRes.send).toBeCalledWith({ member_detail: { id: '1', username: 'KF1' } });
  });
});

unit test results with coverage report:

 PASS  stackoverflow/61834610/retrieveMember.test.js (9.346s)
  61834610
    ✓ should throw 400 error if id is empty string (18ms)
    ✓ should throw 400 error if id is undefined (2ms)
    ✓ should throw 400 error if id is invalid format (1ms)
    ✓ should retrieve one member by id and send response correctly (1ms)

  console.debug
    could not get member

      at stackoverflow/61834610/retrieveMember.js:27:7

  console.debug
    could not get member

      at stackoverflow/61834610/retrieveMember.js:27:7

  console.debug
    could not get member

      at stackoverflow/61834610/retrieveMember.js:27:7

-------------------|---------|----------|---------|---------|-------------------
File               | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------------|---------|----------|---------|---------|-------------------
All files          |   95.83 |     87.5 |      75 |   95.45 |                   
 MemberService.js  |   66.67 |      100 |       0 |   66.67 | 3                 
 generateError.js  |     100 |      100 |     100 |     100 |                   
 retrieveMember.js |     100 |     87.5 |     100 |     100 | 26                
-------------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       4 passed, 4 total
Snapshots:   0 total
Time:        10.539s

Upvotes: 17

Related Questions