Search code examples
reactjsjestjsenzymeredux-form

How to test a redux form with async validation enabled


I enabled async validation for a field in my redux form. I use jest and enzyme to test form submission.

I tried mocking the async validation function with a simple resolved promise, the form still can not be submitted. But I removed async validation, the form can be submitted without any problems.

...
jest.mock('../../../../../../utilities/validators');

it('should set registration info and set current step with correct values when registration form is successfully submitted', () => {
    const store = createStore(
      combineReducers({
        form: formReducer,
      }),
    );

    validateEmailUnique.mockImplementation(() => Promise.resolve());

    const mockOnSetRegistrationInfo = jest.fn();
    const mockOnSetRegistrationCurrentStep = jest.fn();

    const updatedProps = {
      ...defaultProps,
      onSetRegistrationInfo: mockOnSetRegistrationInfo,
      onSetRegistrationCurrentStep: mockOnSetRegistrationCurrentStep,
    };

    const wrapper = mount(
      <Provider store={store}>
        <StepOne {...updatedProps} />
      </Provider>,
    );

    const form = wrapper.find('form');
    const businessEmailTextField = wrapper.find(
      'input#business-email-text-field',
    );

    businessEmailTextField.simulate('change', {
      target: {
        value: '[email protected]',
      },
    });

    form.simulate('submit');

    expect(mockOnSetRegistrationInfo).toHaveBeenCalled();

I expect the form to be submitted and then the 'onSetRegistrationInfo' function which is inside the form submitted callback function to be called. However, since the async validation is not passed, the form can not be submitted during test.


Solution

  • The issue is that the async validation hasn't completed by the time the expect runs and fails.

    From what I can see of your code it doesn't look like you have direct access to the Promise from your async validation step so you won't be able to await it directly...

    ...but if you've mocked any async operations to resolve immediately then they should all complete in one cycle of the Promise microtask queue.

    If that is the case then you can move your assertions to something like setImmediate or setTimeout and use done to let Jest know when the test is complete:

    it('should set registration info...', done => {  // <= use done
    
      // ...
    
      form.simulate('submit');
    
      setImmediate(() => {
        expect(mockOnSetRegistrationInfo).toHaveBeenCalled();  // Success!
        done();  // <= now call done
      });
    });