Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I fail a test in Jest when an uncaught promise rejection occurs?

I'm working on adding test coverage to a Node project I'm working on using Jest. The code I'm testing is throwing errors within promises resulting in an UnhandledPromiseRejectionWarning message being logged to the console.

While writing tests, I can pretty easily identify these issues and resolve them, but these warnings aren't actually causing Jest to mark the tests as failed, so our CI won't catch it. I've searched around for any suggestions and haven't found much.

I did find in Node's documentation that you can catch these warnings and handle them...

process.on('unhandledRejection', (error) => {
  throw error; // Or whatever you like...
});

So it seems like it would be pretty straightforward to add this code into my test cases. After all, an Error thrown within the test should cause the test to fail...

describe('...', () => {
  it('...', () => {
    process.on('uncaughtRejection', (error) => {
      throw error;
    });

    // the rest of my test goes here
  });
});

Unfortunately the behavior I'm seeing is that the error does get thrown, but Jest doesn't catch it and fail the test. Instead, Jest crashes with this error and the tests don't continue to run. This isn't really desirable, and seems like incorrect behavior.

Throwing an error outside of the uncaughtRejection handler works as expected: Jest logs the thrown error and fails the test, but doesn't crash. (i.e. the test watcher keeps watching and running tests)

like image 636
Jesse Dunlap Avatar asked May 01 '18 17:05

Jesse Dunlap


People also ask

How do you resolve a promise in Jest?

You can also use the . resolves matcher in your expect statement, and Jest will wait for that promise to resolve. If the promise is rejected, the test will automatically fail. return expect(fetchData()).

How do you reject a promise in Jest?

To test for a rejected promise using Jest, we can use the rejects. toEqual method. it('rejects...', () => { const Container = createUserContainer(CreateUser); const wrapper = shallow(<Container />); expect(wrapper.

What is a unhandled promise rejection?

When a promise is rejected, it looks for a rejection handler. If it finds one, like in the example above, it calls the function with the error. Perfect. If there's no rejection handler, the promise throws up its hands and produces a global unhandled rejection error.

How do you test a function in Jest?

Jest running tests Tests are run with npm test command. The test files must have the test term in their names. $ npm test > [email protected] test C:\Users\Jano\Documents\js\jest-test > jest PASS ./math-utils. test.


3 Answers

The way I've approached this is very much tied into the way I write my functions - basically, any function that uses promises should return a promise. This allows whatever code calls that function to handle catching errors in any way it sees fit. Note that this is my approach and I'm not going to claim this is the only way to do things.

For example... Imagine I'm testing this function:

const myFunction = () => {
    return doSomethingWithAPromise()
        .then(() => {
            console.log('no problems!');
            return true;
        });
};

The test will look something like this:

describe('...', () => {
    it('...', () => {
        return myFunction()
            .then((value) => {
                expect(value).toBe(true);
            });
    });
});

Which works great. Now what happens if the promise is rejected? In my test, the rejected promise is passed back to Jest (because I'm returning the result of my function call) and Jest can report on it.

If, instead, your function does not return a promise, you might have to do something like this:

const myOtherFunction = () => {
    doSomethingWithAPromise()
        .then(() => {
            console.log('no problems!');
            return true;
        })
        .catch((err) => {
             // throw the caught error here
             throw err;
        });
};

Unlike the example above, there is no (direct) way for Jest to handle a rejected promise because you're not passing the promise back to Jest. One way to avoid this might be to ensure there is a catch in the function to catch & throw the error, but I haven't tried it and I'm not sure if it would be any more reliable.

like image 192
Kryten Avatar answered Oct 31 '22 18:10

Kryten


Include the following content in Jest's setupFiles:

if (!process.env.LISTENING_TO_UNHANDLED_REJECTION) {
  process.on('unhandledRejection', reason => {
    throw reason
  })
  // Avoid memory leak by adding too many listeners
  process.env.LISTENING_TO_UNHANDLED_REJECTION = true
}

Courtesy of stipsan in https://github.com/facebook/jest/issues/3251#issuecomment-299183885.

like image 37
Aleksi Avatar answered Oct 31 '22 17:10

Aleksi


module:

export function myPromise() {
  return new Promise((resolve, reject) => {
    const error = new Error('error test');
    reject(error);
  });
}

test:

import { myPromise } from './module';


it('should reject the promise', () => {
  expect.assertions(1);

  const expectedError = new Error('error test');

  myPromise().catch((error) => {
    expect(error).toBe(expectedError);
  });
like image 30
Luca Borrione Avatar answered Oct 31 '22 19:10

Luca Borrione