Search code examples
javascriptreactjsreact-reduxvitest

Vitest React Redux - expected "spy" to be called with arguments


i getting error like this when I want to test my React application which uses Redux, I want to test the thunk function register AssertionError: expected "spy" to be called with arguments: [ [Function] ]

// action.js
function asyncRegisterUser({ name, email, password }) {
    return async (dispatch) => {
        dispatch(showLoading());
        try {
            await api.register({ name, email, password });
        } catch (error) {
            alert(error.message);
        }
        dispatch(hideLoading());
    };
}

export {
    asyncRegisterUser,
};

// action.test.js

import { hideLoading, showLoading } from 'react-redux-loading-bar';
import {
 afterEach, beforeEach, describe, expect, it, vi,
} from 'vitest';
import api from '../../utils/api';
import { asyncRegisterUser } from './action';

const fakeRegisterResponse = {
    id: 'john_doe',
    name: 'John Doe',
    email: '[email protected]',
    avatar: 'https://generated-image-url.jpg',
};

const fakeRegisterInput = {
    name: 'John Doe',
    email: '[email protected]',
    password: 'JohnDoe',
};

const fakeErrorResponse = new Error('Ups, something went wrong');

describe('asyncRegisterUser thunk', () => {
    beforeEach(() => {
        api._registerUser = api.register;
    });

    afterEach(() => {
        api.register = api._registerUser;

        delete api._registerUser;
    });

    it('should dispatch action correctly when register success', async () => {
        api.register = () => Promise.resolve(fakeRegisterResponse);
        const dispatch = vi.fn();

        await asyncRegisterUser(fakeRegisterInput)(dispatch);

        expect(dispatch).toHaveBeenCalledWith(showLoading());
        expect(dispatch).toHaveBeenCalledWith(
            asyncRegisterUser(fakeRegisterInput), //ERROR HERE
        );
        expect(dispatch).toHaveBeenCalledWith(hideLoading());
    });

    it('should dispatch action and call alert correctly when register failed', async () => {
        api.register = () => Promise.reject(fakeErrorResponse);

        const dispatch = vi.fn();

        window.alert = vi.fn();

        await asyncRegisterUser(fakeRegisterInput)(dispatch);

        expect(dispatch).toHaveBeenCalledWith(showLoading());
        expect(dispatch).toHaveBeenCalledWith(hideLoading());
        expect(window.alert).toHaveBeenCalledWith(fakeErrorResponse.message);
    });
});

i wanna make test using vitest on react redux thunk function when i call register function, should dispatch action correctly when register success


Solution

  • In your asyncRegisterUser action creator the dispatch function is only called twice, once with showLoading() and a second time with hideLoading().

    function asyncRegisterUser({ name, email, password }) {
      return async (dispatch) => {
        dispatch(showLoading()); // <-- dispatch call #1
        try {
          await api.register({ name, email, password });
        } catch (error) {
          alert(error.message);
        }
        dispatch(hideLoading()); // <-- dispatch call #2
      };
    }
    

    It would probably be very bad for your asyncRegisterUser action to trigger calling itself so probably want to assert that this does not happen.

    it('should dispatch action correctly when register success', async () => {
      api.register = () => Promise.resolve(fakeRegisterResponse);
      const dispatch = vi.fn();
    
      await asyncRegisterUser(fakeRegisterInput)(dispatch);
    
      expect(dispatch).toHaveBeenCalledWith(showLoading()); // <-- dispatch #1
    
      // assert asyncRegisterUser action isn't recursively called
      expect(dispatch).not.toHaveBeenCalledWith(
        asyncRegisterUser(fakeRegisterInput),
      );
    
      expect(dispatch).toHaveBeenCalledWith(hideLoading()); // <-- dispatch #2
    });
    

    Otherwise, remove the middle assertion since dispatch obviously doesn't call asyncRegisterUser.

    it('should dispatch action correctly when register success', async () => {
      api.register = () => Promise.resolve(fakeRegisterResponse);
      const dispatch = vi.fn();
    
      await asyncRegisterUser(fakeRegisterInput)(dispatch);
    
      expect(dispatch).toHaveBeenCalledWith(showLoading()); // <-- dispatch #1
      expect(dispatch).toHaveBeenCalledWith(hideLoading()); // <-- dispatch #2
    });