0

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!

diegoalmesp
  • 162
  • 1
  • 13
  • 1
    What if you change `window.fetch` to `global.fetch`? – Lance Harper Feb 26 '18 at 18:18
  • Hi @LanceHarper ! thanks for your response. It does the same, I'm also using `import fetch from 'isomorphic-fetch';` in the api file. – diegoalmesp Feb 26 '18 at 18:24
  • 1
    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 response – Lance Harper Feb 26 '18 at 18:32
  • check this https://stackoverflow.com/questions/48172819/testing-dispatched-actions-in-redux-thunk-with-jest/48227479#48227479 – Shubham Khatri Feb 26 '18 at 18:42
  • 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 need – diegoalmesp Feb 27 '18 at 12:39
  • @LanceHarper I just created src/setupTest.js (I'm using `create-react-app` as the docs says (https://www.npmjs.com/package/jest-fetch-mock), and I'm not supposed to edit the package.json. Should I update `window.fetch=...` to `global.fetch=...`? because I'm still getting the real data instead of the mocked one – diegoalmesp Feb 27 '18 at 12:59

1 Answers1

0

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();
    });
  });
Dakota
  • 1,254
  • 8
  • 16
  • Hi @Dakota ! thanks for your response, as I said I'm new with Jest and Unit testing in general, but this looks like what I'm trying to do. Could you please tell me what the `Q.resolve(person)` line means? is Q a node package? or it's a query...? I got lost there. Thanks again! – diegoalmesp Feb 27 '18 at 11:35
  • Q is a promise library I totally glossed over that. It works the same as a normal promise. – Dakota Feb 28 '18 at 12:33