How do I properly test for a rejected promise using Jest?

43,601

Solution 1

You should do something like this:

it('rejects...', () => {
  const Container = createUserContainer(CreateUser);
  const wrapper = shallow(<Container />);  
  return expect(wrapper.instance().handleFormSubmit()).rejects.toEqual('error');
});

I think it is cleaner this way. You can see this approach in the official docs.

Solution 2

The test fails because it's not aware that the subject is asynchronous. It can be fixed by using a done param or making the test function async.

Note it's also necessary to set the number of expected assertions so that the test will fail even if the catch branch is not taken.

async/await style:

 it('rejects...', async () => {
    expect.assertions(1);
    const Container = createUserContainer(CreateUser);
    const wrapper = shallow(<Container />); 

    await wrapper.instance().handleFormSubmit()
      .catch(e => {
         console.log("State: ", wrapper.state());
         expect(e).toEqual('error');
      });
 });

Older style done param:

 it('rejects...', done => {
    expect.assertions(1);
    const Container = createUserContainer(CreateUser);
    const wrapper = shallow(<Container />);  

    wrapper.instance().handleFormSubmit()
      .catch(e => {
         console.log("State: ", wrapper.state());
         expect(e).toEqual('error');
         done();
      });
 });

Asynchronous Testing Reference

expect.assertions reference

Solution 3

Your code looks correct. Why do you say that it doesn't wait for the Promise to reject? The only difference I would make would be to make use of Jest's mocking capability, so change

Mock

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

to

Test

jest.mock('../services');
const services = require('../services');

const createUser = jest.spyOn(services, "createUser");
createUser.mockRejectedValue("error");

...

it('rejects...', () => {

There's no need to have a separate Mock file

Share:
43,601
coding123
Author by

coding123

Updated on August 23, 2022

Comments

  • coding123
    coding123 over 1 year

    Code

    import { createUser } from '../services';
    ...
    ...
    
    handleFormSubmit = () => {  
      this.setState({ loading: true });
      createUser()
        .then(() => {
          this.setState({
            loading: false,
          });
        })
        .catch(e => {
           this.setState({
             error: e,
           });
        });
    };
    

    Test

     it('rejects...', () => {
        const Container = createUserContainer(CreateUser);
        const wrapper = shallow(<Container />);  
    
        return wrapper.instance().handleFormSubmit()
          .catch(e => {
             console.log("State: ", wrapper.state());
             expect(e).toEqual('error');
          });
     });
    

    Mock

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

    The test does force the code to go into the catch in the method. So the state does get set to 'error'.

    But in my test, it doesn't do what I expect and wait for the Promise to reject before it tests for the state change. I'm not sure what to try here, should I be using async/await?

    So it's the createUser method I want to wait for but I'm not sure my implementation allows for this.

  • coding123
    coding123 almost 6 years
    this didn't solve the issue. The code still goes into the catch, but the test still does not wait for the promise to fulfill before running the expects
  • Hriday Modi
    Hriday Modi almost 6 years
    My bad, Have updated the answer with documentation link. It will surely solve your problem
  • Shiraz
    Shiraz almost 5 years
    Note that your test can also confirm that state.loading is still set to true
  • BenjiFB
    BenjiFB over 4 years
    This was VERY helpful to me. The ".catch" style was exactly what I needed. Thanks!
  • oyalhi
    oyalhi over 4 years
    wouldn't this test give false positive if the function didn't throw?
  • A F
    A F about 4 years
    @oyalhi no, it won't give a false positive. The line expect.assertions(1) tells jest that there will be an assertion so if the catch isn't triggered, jest will complain about the missing assertion
  • Brian Sterling
    Brian Sterling almost 4 years
    What if the target code of the test needed to use "new services" thus you needed to do jest.doMock with a requireActual and override just a single function?
  • Technotronic
    Technotronic about 3 years
    This is the official way to check async errors. This should be the accepted answer.
  • Ahmed-Anas
    Ahmed-Anas over 2 years
    Note: should probably make the method async and add await before expect (see linked official docs)
  • Andres Gardiol
    Andres Gardiol over 2 years
    @Ahmed-Anas see the first example in the link. The one that starts with t('tests error with rejects', () => { not the other.
  • Jordi
    Jordi over 2 years
    I had to make make a slight variation to make this work: expect(wrapper.instance().handleFormSubmit()).rejects.toEqua‌​l(Error('error));
  • Andres Gardiol
    Andres Gardiol over 2 years
    @Jordi that depends on what your tested method returns
  • Dave Amphlett
    Dave Amphlett about 2 years
    I've had success with using the syntax: expect(wrapper.instance().handleFormSubmit()).rejects.toThro‌​w('error text')