Search code examples
javascriptreactjsunit-testingjestjses6-promise

How to unit test promise rejection in React and Jest


I am trying to write a unit test for a react component. It's a fairly standard component which calls a promise-returning method and uses 'then' and 'catch' to handle the resolution. My test is trying to validate that it calls the correct method when the promise is rejected however despite following what i believe is a standard patttern, I cannot get jest to validate the call. I have listed the relevant files here and have also put up a github sample, which is linked at the bottom of the question. That sample is simply a new react app created using npx and the files below added in.

Here is my example component:

import React from 'react';
import api from '../api/ListApi';

class ListComponent extends React.Component {

    constructor(props) {
        super(props);
        this.fetchListSuccess = this.fetchListSuccess.bind(this); 
        this.fetchListFailed = this.fetchListFailed.bind(this); 

    }

    fetchList() {
        api.getList()
        .then(this.fetchListSuccess)
        .catch(this.fetchListFailed);
    }

    fetchListSuccess(response) {
        console.log({response});
    };

    fetchListFailed(error) {
        console.log({error});
    };

    render() {
        return(<div>Some content</div>);
    };
}

export default ListComponent;

Here is the api class (note, the api doesnt exist if you run the app, its just here for example):

const getList = () => fetch("http://someApiWhichDoesNotExist/GetList");

export default { getList };

And here is the test case:

import ListComponent from './ListComponent';
import api from '../api//ListApi';

describe('ListComponent > fetchList() > When the call to getList fails', () => {
    it('Should call fetchListFailed with the error', async () => {

        expect.hasAssertions();

        //Arrange
        const error = { message: "some error" };
        const errorResponse = () => Promise.reject(error);
        const componentInstance = new ListComponent();

        api.getList = jest.fn(() => errorResponse());
        componentInstance.fetchListFailed = jest.fn(() => { });

        //Act
        componentInstance.fetchList();

        //Assert
        try {
            await errorResponse;
        } catch (er) {
            expect(componentInstance.fetchListFailed).toHaveBeenCalledWith(error);
        }

    });
});

The problem is that the test is not executing the catch block, so, in this case, the expect.hasAssertions() is failing the test. Can anyone help me understand the catch block is not executing? Wrapping the await in the try block and asserting in the catch seems to be a standard pattern in the docs but I am fairly new to Js and React and am obviously doing something wrong.

Here is the sample project on GitHub. Any help would be greatly appreciated =)


Solution

  • In your console:

    const errorResponse = () => Promise.reject();
    await errorResponse;
    //() => Promise.reject()
    

    You're awaiting a function, not the result of the call to that function. You want to:

    await errorResponse();
    

    EDIT:

    In addition to that, the rest of your test is confusing. I believe you actually want to test what happens when the fetchList method of your component is called, and it fails, I assume. So you need to call it in your test, and await it's response:

    1. Update your component's fetchList method to return the promise.
    2. await componentInstance.fetchList() instead of await errorResponse()
    3. Because you catch the error in fetchList you'll never enter the catch or the try...catch so your final test should look like this:

    Test:

    //Arrange
    const error = { message: "some error" };
    const errorResponse = () => Promise.reject(error);
    const componentInstance = new ListComponent();
    
    api.getList = jest.fn(() => errorResponse());
    componentInstance.fetchListFailed = jest.fn(() => { });
    
    //Act
    await componentInstance.fetchList();
    expect(componentInstance.fetchListFailed).toHaveBeenCalledWith(error);
    

    Component:

    fetchList() {
        return api.getList()
        .then(this.fetchListSuccess)
        .catch(this.fetchListFailed);
    }