0
votes

Im starting with unit testing and Jest. What I want is to test the action's response after fetching some resources from the db.

This is the action code:

export function loadPortlets() {
   return function(dispatch) {
     return portletApi.getAllPortlets().then(response => {
       dispatch(loadPortletsSuccess(response));
       dispatch(hideLoading());
     }).catch(error => {
        dispatch({ type: null, error: error });
        dispatch(hideLoading());
        throw(error);
     });
   };
}

This code is fetching data from:

  static getAllPortlets() {

    return fetch(`${API_HOST + API_URI}?${RES_TYPE}`)
      .then(response =>
        response.json().then(json => {
          if (!response.ok) {
            return Promise.reject(json);
          }

          return json;
        })
      );
}

And this is the test:

import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import fetch from 'isomorphic-fetch';
import fetchMock from 'fetch-mock';
import * as actions from '../portletActions';
import * as types from '../actionTypes';

const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);

const mockResponse = (status, statusText, response) => {
  return new window.Response(response, {
    status: status,
    statusText: statusText,
    headers: {
      'Content-type': 'application/json'
    }
  });
};

describe('async actions', () => {
  afterEach(() => {
    fetchMock.reset();
    fetchMock.restore();
  })

  it('calls request and success actions if the fetch response was successful', () => {
    window.fetch = jest.fn().mockImplementation(() =>
      Promise.resolve(mockResponse(200, null, [{ portlets: ['do something'] }])));

    const store = mockStore({ portlets: []});

    return store.dispatch(actions.loadPortlets())
      .then(() => {
        const expectedActions = store.getActions();
        expect(expectedActions[0]).toContain({ type: types.LOAD_PORTLETS_SUCCESS });
      })
  });

});

And this is the result of running the test:

FAIL  src\actions\__tests__\portletActions.tests.js                                                                                                                      
  ● async actions › calls request and success actions if the fetch response was successful                                                                                

    expect(object).toContain(value)                                                                                                                                       

    Expected object:                                                                                                                                                      
      {"portlets": [// here an array of objects], "type": "LOAD_PORTLETS_SUCCESS"}                                                            
    To contain value:                                                                                                                                                     
      {"type": "LOAD_PORTLETS_SUCCESS"}                                                                                                                                   

      at store.dispatch.then (src/actions/__tests__/portletActions.tests.js:56:34)
      at <anonymous>
      at process._tickCallback (internal/process/next_tick.js:188:7)

In the redux docs for this example (https://redux.js.org/recipes/writing-tests), they receive a result containing only the action types executed, but I'm getting the real data and the action inside the array.

So I'm not sure if the code is wrong, or the test, or both!

Thanks in advance, any help is highly appreciated!

1
What if you change window.fetch to global.fetch?Lance Harper
Hi @LanceHarper ! thanks for your response. It does the same, I'm also using import fetch from 'isomorphic-fetch'; in the api file.diegoalmesp
Are you using a setupJest.js file? With a setupJest.js file configured, you could assign global.fetch = require('jest-fetch-mock'). Then in your tests you can use fetch.mockResponse(JSON.stringify({ ... }) to assign the expected responseLance Harper
Thanks @LanceHarper I dont have a setupJest.js file, where should I put that? just create and import in my tests? I'm looking for an example but the ones I found look very advanced for what I needdiegoalmesp

1 Answers

0
votes

You're testing too much with this unit test. I see you are using thunks it looks like so you can change your fetch to be passed as a module to the thunk and do something like this. I used jasmine but it's basically the same thing. You don't want to mock your store here just the action and dispatch. The point of the unit test should be to test the async action, not to test getting real data from the db or redux store interactions so you can stub all that out.

For reference configureStore would look like this...

const createStoreWithMiddleware = compose(
  applyMiddleware(thunk.withExtraArgument({ personApi }))
)(createStore);

And the test case...

  it('dispatches an action when receiving', done => {
    const person = [{ firstName: 'Francois' }];
    const expectedAction = {
      type: ActionTypes.RECEIVED,
      payload: {
        people,
      },
    };

    const dispatch = jasmine.createSpy();
    const promise = Q.resolve(person);
    const personApi = {
      fetchPerson: jasmine
        .createSpy()
        .and.returnValue(promise),
    };

    const thunk = requestPerson();
    thunk(dispatch, undefined, { personApi });

    promise.then(() => {
      expect(dispatch.calls.count()).toBe(2);
      expect(dispatch.calls.mostRecent().args[0]).toEqual(expectedAction);
      done();
    });
  });