Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

spying a function called in a rejected promise

I would like to spy a function to test if this function is called in a catch block when a promise is rejected. My code is a react component like this

export class ResetPassword extends Component {
    handleSubmit = e => {
        e.preventDefault();

        this.props
            .resetPassword()
            .then(() => {
                this.props.history.push(LOGIN_PATH);
            })
            .catch(({ error }) => {
                this.props.displayErrorAlert('impossible to change password. You should ask for a new reset password link',6000);
            });
    };
}

Here I want to test if the function displayErrorAlert has been called. I made this test

it('validate form', () => {
    const resetPassword = () => {
        return Promise.reject({
            error: {
                response: {
                    data: {
                        errors: [
                            {
                                title: 'foo',
                            },
                        ],
                    },
                },
            },
        });
    };

    const displaySpy = sinon.spy();

    const wrapper = mount(
        <ResetPassword
            history={{}}
            resetPassword={resetPassword}
            displayErrorAlert={displaySpy}
        />
    );

    wrapper.instance().handleSubmit({
        preventDefault: () => {},
    });

    expect(displaySpy.calledOnce).toEqual(true);
});

The spy is called but asynchronously of course so my test always fails. I would like to find a way to test if the function has been called only once the catch block has been called and I have no idea how to do that.

like image 879
manu Avatar asked Oct 24 '25 04:10

manu


2 Answers

Sinon provides you everything you need when handling promises, you can resolve and reject a stubbed promise using sinon.stub().

const resetPassword = sinon.stub();
const displayErrorAlert = sinon.spy();
const preventDefault = sinon.spy();

const props = {
  resetPassword,
  displayErrorAlert,
  history: []
};

describe('Given a component', () => {
  let component;

  describe('when rendered', () => {
    beforeAll(() => {
      component = shallow(<ResetPassword {...props} />);
    });

    describe('and the form is submitted and there is an error reseting the password', () => {
      beforeAll(() => {
        resetPassword.rejects(new Error('Oops!'));
        component.find('button').simulate('click', { preventDefault });
      });

      it('should invoke the displayErrorAlert function', () => {
        expect(displayErrorAlert.calledOnce).toBeTruthy();
      });
    });
  });
});
like image 99
dcodesmith Avatar answered Oct 26 '25 16:10

dcodesmith


I found an other solution, I return the promise in the handleSubmit function and use it in my tests.

export class ResetPassword extends Component {
    handleSubmit = e => {
        e.preventDefault();

        return this.props
            .resetPassword()
            .then(() => {
                this.props.history.push(LOGIN_PATH);
            })
            .catch(({ error }) => {
                this.props.displayErrorAlert('impossible to change password. You should ask for a new reset password link',6000);
            });
    };
}

and my test

it('validate form', () => {
    const resetPassword = () => {
        return Promise.reject({
            error: {
                response: {
                    data: {
                        errors: [
                            {
                                title: 'foo',
                            },
                        ],
                    },
                },
            },
        });
    };

    const displaySpy = sinon.spy();

    const wrapper = mount(
        <ResetPassword
            history={{}}
            resetPassword={resetPassword}
            displayErrorAlert={displaySpy}
        />
    );

    expect.assertions(1);
    const promise = wrapper.instance().handleSubmit({
        preventDefault: () => {},
    });

    return promise.then(() => {
        expect(displaySpy.calledOnce).toEqual(true);
    });

});
like image 31
manu Avatar answered Oct 26 '25 18:10

manu