Search code examples
javascriptunit-testingexpresschaisinon

How to test that express middleware throws an error by calling next(error)


I had tried to catch the error by stub the next() but it fails.

Here's the function

async getUser (req, res, next) {
  try {
    if (!req.user) {
      throw new CustomError('找不到使用者', 404)
    } else {
      // do something
    }
  } catch (err) {
    next(err)
  }
}

and the unit test part

it('no user data => Error 404', async () => {
  const res = mockedResponse()
  const next = sinon.stub()
  await getUser({}, res, next)
  expect(next.getCall(0).args[0]).to.deep.equal(new CustomError('cannnot find user', 404))
})

However the test result shows that

AssertionError: expected [Error: cannnot find user] to deeply equal [Error: cannnot find user]

Is there a better way to catch that CustomError throw by the function?


Solution

  • deep-eql algorithm doesn't do deep equality on errors. See https://github.com/chaijs/chai/issues/1065#issuecomment-337857345

    Here is the solution:

    index.js:

    const CustomError = require('./customError');
    
    const controller = {
      async getUser(req, res, next) {
        try {
          if (!req.user) {
            throw new CustomError('找不到使用者', 404);
          } else {
            // do something
          }
        } catch (err) {
          next(err);
        }
      },
    };
    
    module.exports = controller;
    

    customError.js:

    class CustomError extends Error {
      constructor(message, code) {
        super(message);
        this.code = code;
      }
    }
    
    module.exports = CustomError;
    

    index.test.js:

    const controller = require('./');
    const CustomError = require('./customError');
    const sinon = require('sinon');
    const { expect } = require('chai');
    
    describe('61879445', () => {
      it('should throw error if user not found', async () => {
        const mNext = sinon.stub();
        const mReq = {};
        const mRes = {};
        await controller.getUser(mReq, mRes, mNext);
        // chai way
        expect(mNext.getCall(0).args[0]).to.be.an.instanceof(CustomError);
        expect(mNext.getCall(0).args[0]).to.have.property('message', '找不到使用者');
        expect(mNext.getCall(0).args[0]).to.have.property('code', 404);
        // sinon way
        sinon.assert.calledWith(
          mNext,
          sinon.match
            .instanceOf(CustomError)
            .and(sinon.match.has('message', '找不到使用者'))
            .and(sinon.match.has('code', 404)),
        );
      });
    });
    

    unit test results with coverage report:

      61879445
        ✓ should throw error if user not found
    
    
      1 passing (15ms)
    
    ----------------|---------|----------|---------|---------|-------------------
    File            | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
    ----------------|---------|----------|---------|---------|-------------------
    All files       |     100 |       50 |     100 |     100 |                   
     customError.js |     100 |      100 |     100 |     100 |                   
     index.js       |     100 |       50 |     100 |     100 | 6                 
    ----------------|---------|----------|---------|---------|-------------------