revenge spirit
revenge spirit

Reputation: 85

how to unit test uncaughtException and unhandledRejection in Nodejs using Jest

I have built an error handler like this:

const logger = require('./logger');

module.exports = () => {
  process.on('uncaughtException', (err) => {
    logger.error(err.message, err);
    process.exit(1);
  });

  process.on('unhandledRejection', (err) => {
    throw err;
  });
};

Now I want to unit test this, mainly the first event listener. My current test looks like this:

const logger = require('../../utils/logger');
require('../../utils/unhandledErrors')();

describe('Uncaught Exceptions', () => {
  it('should log the exception and exit the process', () => {
    process.exit = jest.fn();
    logger.error = jest.fn();
    jest.fn(() => {
      throw new Error('fake error');
    })();

    expect(logger.error).toBeCalledTimes(1);
    expect(process.exit).toBeCalledTimes(1);
    expect(process.exit).toBeCalledWith(1);
  });
});

The code above fails at throw new error line with no useful information logged to console by jest despite the --verbose flag.
Any suggestions on how to unit test such modules? I have looked through the link below but found nothing.
How to test a unhandledRejection / uncaughtException handler with jest

Upvotes: 1

Views: 2574

Answers (1)

Lin Du
Lin Du

Reputation: 102577

My test strategy is to install spy onto process.on(), process.exit() and console.error methods using jest.spyOn(object, methodName). After doing this, these methods have no side effects. Then, you can test your code logic in an isolated environment.

E.g.

unhandledErrors.js:

module.exports = () => {
  process.on('uncaughtException', (err) => {
    console.error(err.message);
    process.exit(1);
  });

  process.on('unhandledRejection', (err) => {
    throw err;
  });
};

unhandledErrors.test.js:

const unhandledErrors = require('./unhandledErrors');

describe('65386298', () => {
  afterEach(() => {
    jest.restoreAllMocks();
  });
  it('should handle uncaughtException', () => {
    const mError = new Error('Server internal error');
    jest.spyOn(process, 'on').mockImplementation((event, handler) => {
      if (event === 'uncaughtException') {
        handler(mError);
      }
    });
    jest.spyOn(console, 'error').mockReturnValueOnce();
    jest.spyOn(process, 'exit').mockReturnValueOnce();
    unhandledErrors();
    expect(process.on).toBeCalledWith('uncaughtException', expect.any(Function));
    expect(process.exit).toBeCalledWith(1);
    expect(console.error).toBeCalledWith('Server internal error');
  });

  it('should handle unhandledRejection', () => {
    const mError = new Error('dead lock');
    jest.spyOn(process, 'on').mockImplementation((event, handler) => {
      if (event === 'unhandledRejection') {
        handler(mError);
      }
    });
    expect(() => unhandledErrors()).toThrowError('dead lock');
    expect(process.on).toBeCalledWith('unhandledRejection', expect.any(Function));
  });
});

unit test result:

 PASS  examples/65386298/unhandledErrors.test.js
  65386298
    ✓ should handle uncaughtException (3 ms)
    ✓ should handle unhandledRejection (1 ms)

--------------------|---------|----------|---------|---------|-------------------
File                | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
--------------------|---------|----------|---------|---------|-------------------
All files           |     100 |      100 |     100 |     100 |                   
 unhandledErrors.js |     100 |      100 |     100 |     100 |                   
--------------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        5.201 s

Upvotes: 5

Related Questions