1

I have a method in controller, as below:

    import { calcu } from '../services/myServices';

    export const getProduct = (req, res, next) => {
      try {
        const { type } = req.params;

        const { id, productCode } = req.body;

        if (!id || !productCode) {
          res.status(400).json({ error: { message: 'Id or productCode is required' } });
        } else {
          switch (type.toUpperCase()) {
            case 'X':
              try {
                const result = calcu(id, productCode);
                res.status(200).json(result);
              } catch (err) {
                res.status(400).json({ error: { message: err.message } });
              }
              break;
            default:
              res.status(400).json({ error: { message: `type ${type} is not support` } });
          }
        }
      } catch (err) {
        next(err);
      }
    };

This my unit test code in this case:

    import { getProduct } from './quotationController';

    describe('Controller', () => {
        let json, res, status;
        test('Should return message error if the id or productCode is missing', () => {
            const req = {
                body: { id: "1111" },
                param: { type: "qqqqq" }
            };
            const next = err => err.message;
            const result = getProduct(req, res, next);
            //expect(result).toBe(undefined);
            expect(result).toEqual({
                code: 400,
                message: 'Id or productCode is required'
            });
        });
    })

I got an error when I ran the unit test code:

result is undefined.

6
  • In your test getProduct(req, res, next); res is undefined but the getProduct function uses it res.status. are you working with express ? Commented Dec 28, 2019 at 9:20
  • yes, I am working with express and I use Jest to make UT code Commented Dec 28, 2019 at 10:10
  • 1
    You can't just call the controllers function in the test because it depends on the entire framework, you need to mock a lot o stuff. terlici.com/2015/09/21/node-express-controller-testing.html try this tutorial it may help Commented Dec 28, 2019 at 10:18
  • Does it solve your problem ? Commented Dec 28, 2019 at 11:00
  • I have not still solve my issue. Commented Dec 28, 2019 at 11:13

1 Answer 1

1

Here is the unit test solution:

controller.js:

import { calcu } from './service';

export const getProduct = (req, res, next) => {
  try {
    const { type } = req.params;
    const { id, productCode } = req.body;

    if (!id || !productCode) {
      res.status(400).json({ error: { message: 'Id or productCode is required' } });
    } else {
      switch (type.toUpperCase()) {
        case 'X':
          try {
            const result = calcu(id, productCode);
            res.status(200).json(result);
          } catch (err) {
            res.status(400).json({ error: { message: err.message } });
          }
          break;
        default:
          res.status(400).json({ error: { message: `type ${type} is not support` } });
      }
    }
  } catch (err) {
    next(err);
  }
};

service.js: (simulated)

export function calcu(id, code) {
  return id + code;
}

controller.test.js:

import { getProduct } from './controller';
import { calcu } from './service';

jest.mock('./service.js', () => ({ calcu: jest.fn() }));

describe('Controller', () => {
  let mRes;
  let mNext;
  beforeEach(() => {
    mRes = { status: jest.fn().mockReturnThis(), json: jest.fn() };
    mNext = jest.fn();
  });
  afterEach(() => {
    jest.resetAllMocks();
  });

  test('Should return message error if the id or productCode is missing', () => {
    const mReq = { body: { id: '1111' }, params: { type: 'qqqqq' } };
    getProduct(mReq, mRes, mNext);
    expect(mRes.status).toBeCalledWith(400);
    expect(mRes.status().json).toBeCalledWith({ error: { message: 'Id or productCode is required' } });
  });

  test('should call next when error happens', () => {
    const mReq = {};
    getProduct(mReq, mRes, mNext);
    expect(mNext).toBeCalledWith(expect.any(Error));
  });

  test('should return message error if type is not support', () => {
    const mReq = { params: { type: 'qqqqq' }, body: { id: '1111', productCode: '22' } };
    getProduct(mReq, mRes, mNext);
    expect(mRes.status).toBeCalledWith(400);
    expect(mRes.status().json).toBeCalledWith({ error: { message: `type ${mReq.params.type} is not support` } });
  });

  test('should return message error if calcu errors', () => {
    const mReq = { params: { type: 'x' }, body: { id: '1111', productCode: '22' } };
    const mError = new Error('calc error');
    calcu.mockImplementationOnce(() => {
      throw mError;
    });
    getProduct(mReq, mRes, mNext);
    expect(calcu).toBeCalledWith('1111', '22');
    expect(mRes.status).toBeCalledWith(400);
    expect(mRes.status().json).toBeCalledWith({ error: { message: mError.message } });
  });

  test('should return correct calc result', () => {
    const mReq = { params: { type: 'x' }, body: { id: '1111', productCode: '22' } };
    calcu.mockReturnValueOnce({ data: 'fake data' });
    getProduct(mReq, mRes, mNext);
    expect(calcu).toBeCalledWith('1111', '22');
    expect(mRes.status).toBeCalledWith(200);
    expect(mRes.status().json).toBeCalledWith({ data: 'fake data' });
  });
});

Unit test result with 100% coverage:

 PASS  src/stackoverflow/59508494/controller.test.js (7.379s)
  Controller
    ✓ Should return message error if the id or productCode is missing (6ms)
    ✓ should call next when error happens (1ms)
    ✓ should return message error if type is not support (1ms)
    ✓ should return message error if calcu errors (2ms)
    ✓ should return correct calc result (2ms)

---------------|----------|----------|----------|----------|-------------------|
File           |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
---------------|----------|----------|----------|----------|-------------------|
All files      |      100 |      100 |      100 |      100 |                   |
 controller.js |      100 |      100 |      100 |      100 |                   |
---------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       5 passed, 5 total
Snapshots:   0 total
Time:        8.731s, estimated 10s

Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59508494

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.