Async redux actions tests always return pending

571 Views Asked by At

I am writing tests for some async actions however the tests are failing because the type which is returned is always REQUEST_PENDING. So even for the tests when the data is fetched the type does not change and the test fails. I am not sure what I am doing wrong.

So the REQUEST_SUCCESS and REQUEST_FAILED are the tests that are always returning REQUEST_PENDING

This is my actions.js

import axios from 'axios';
import {
  REQUEST_PENDING,
  REQUEST_SUCCESS,
  REQUEST_FAILED,
} from './constants';

export const setSearchField = (payload) => ({ type: SEARCH_EVENT, payload });

export const requestRobots = () => {
  return async (dispatch) => {
    dispatch({
      type: REQUEST_PENDING,
    });
    try {
      const result = await axios.get('//jsonplaceholder.typicode.com/users');
      dispatch({ type: REQUEST_SUCCESS, payload: result.data });
    } catch (error) {
      dispatch({ type: REQUEST_FAILED, payload: error });
    }
  };
};

and this is my actions.test.js

import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import {
  REQUEST_PENDING,
  REQUEST_SUCCESS,
  REQUEST_FAILED,
} from './constants';
import * as actions from './actions';

const mock = new MockAdapter(axios);
const mockStore = configureMockStore([thunk]);
const payload = [
  {
    id: 1,
    name: 'robocop',
    email: '[email protected]',
    key: 1,
  },
];


describe('handles requestRobots', () => {
  beforeEach(() => {
    // Runs before each test in the suite
    store.clearActions();
  });

  const store = mockStore();
  store.dispatch(actions.requestRobots());
  const action = store.getActions();

  it('Should return REQUEST_PENDING action', () => {
    expect(action[0]).toEqual({
      type: REQUEST_PENDING,
    });
  });


  it('Should return REQUEST_SUCCESS action', () => {
    mock.onGet('//jsonplaceholder.typicode.com/users').reply(200, {
      data: payload,
    });


    return store.dispatch(actions.requestRobots()).then(() => {
      const expectedActions = [
        {
          type: REQUEST_SUCCESS,
          payload: {
            data: payload,
          },
        },
      ];
      expect(store.getActions()).toEqual(expectedActions);
    });
  });

  it('Should return REQUEST_FAILURE action', () => {
    mock.onGet('//jsonplaceholder.typicod.com/users').reply(400, {
      data: payload,
    });


    return store.dispatch(actions.requestRobots()).then(() => {
      const expectedActions = [
        {
          type: REQUEST_FAILED,
          payload: {
            data: ['Error: Request failed with status code 404'],
          },
        },
      ];
      expect(store.getActions()).toEqual(expectedActions);
    });
  });
});
1

There are 1 best solutions below

1
On BEST ANSWER

The lifecycle of a thunk action is that it will dispatch the REQUEST_PENDING action at the start of every call and then dispatch a REQUEST_FAILED or REQUEST_SUCCESS action at the end.

In your second and third test cases, the store.getActions() array actually has two elements: the pending action and the results action. You need to expect that the actions is an array with both. The REQUEST_FAILED and REQUEST_SUCCESS actions are there, but you aren't seeing them because they are the second element.

Define your pendingAction as a variable since you'll need it in all three tests.

const pendingAction = {
  type: REQUEST_PENDING
}

Then include it in your expectedActions array.

const expectedActions = [
  pendingAction,
    {
      type: REQUEST_SUCCESS,
      payload: {
        data: payload
      }
    }
]

This will cause your success test to pass. I did a quick run of the tests and the failure test still fails because it is not properly mocking the API failure. Right now it is returning a success because the requestRobots function uses the real axios object and not the mock axios adapter. But maybe something in your environment handles this differently.