1
votes

I am trying to learn/implement jest testing into my react-redux application. My test fails saying that the received does not equal what was expected, however, the actual thunk works and returns data to my application. So I've either written the test incorrectly (which i basically copy/pasted from the redux-docs) or I'm writing my thunk incorrectly.

ACTION


export const getOddGroups = () => {
    return dispatch => {
        return axios.get("/api/tables/oddgroups")
        .then(results => {
            dispatch({type: "GET_ODD_GROUPS", payload: results.data})
        }).catch(err => {
            dispatch({ type: "GET_ERRORS", payload: err.response.message })
        })
    }
}

TEST

import configureMockStore from 'redux-mock-store'
import thunk from 'redux-thunk'
import * as oddActions from '../actions/OddActions';
import fetchMock from 'fetch-mock'


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


describe('query preview async actions', () => {
    afterEach(() => {
        fetchMock.restore()
    })

    it('creates GET_ODD_GROUPS when successful', () => {
        fetchMock.get("*", {
            results: { data: [{ "row1": "some data" }] },
            headers: { 'content-type': 'application/json' }
        })

        const expectedActions = [
            { type: "GET_ODD_GROUPS", results: { data: [{ "row1": "some data" }] } },
        ]
        const store = mockStore({ oddGroups: [] })

        return store.dispatch(oddActions.getOddGroups()).then(() => {
            // return of async actions
            expect(store.getActions()).toEqual(expectedActions)
        })
    })
})

TEST RESULT OUTPUT:

 expect(received).toEqual(expected) // deep equality

    - Expected
    + Received

      Array [
        Object {
    -     "results": Object {
    -       "data": Array [
    -         Object {
    -           "row1": "some data",
    -         },
    -       ],
    -     },
    -     "type": "GET_ODD_GROUPS",
    +     "payload": undefined,
    +     "type": "GET_ERRORS",
        },
      ]

EDIT - UPDATE At the suggestion of @CoryDanielson I reworked the test using axios-mock-adapter and this post but I'm still getting the same error as above.

import configureMockStore from 'redux-mock-store'
import thunk from 'redux-thunk'
import * as oddActions from '../actions/oddActions';
import axios from "axios";
import MockAdapter from 'axios-mock-adapter';

const middlewares = [thunk]
const mockStore = configureMockStore(middlewares)
let mock = new MockAdapter(axios);

describe('query preview async actions', () => {

    beforeEach(function () {
        /*Not sure which one is best to use in this situation yet
        * will test both
        */

        mock.reset(); // reset both registered mock handlers and history items with reset
        //mock.restore(); //restore the original adapter (which will remove the mocking behavior)
    });

    it("return data for GET_ODD_GROUPS when successful", function (done) {
        mock.onGet("api/tables/oddGroups")
            .reply(function () {
                return new Promise(function (resolve, reject) {
                    resolve([200, { key: 'value' }]);
                });
            });

        const store = mockStore({ oddGroups: [] })
        store.dispatch(oddActions.getOddGroups()).then(() => {
            let expectedActions = [{ type: "GET_ODD_GROUPS", payload: { key: 'value' } }]
            console.log(store.getActions());
            expect(store.getActions()).toEqual(expectedActions);
        });
        setTimeout(() => {
            done();
        }, 1000)
    });
});

LOGGING:

When I return the console state console.log(store.getActions()); Its giving me back the error dispatch action

And this console.log(store.dispatch(oddActions.getOddGroups())); returns Promise { <pending> }

FINAL SOLUTION:

After trying and failing with several options, I dropped using axios-mock-adapter and used moxios instead. After following this article I was able to successfully create tests.

1
Your fetch mock doesn't seem to line up with what your thunk expects. Your thunk is looking for results.data but your fetchMock is returning payload or body.payload - Add some logging inside of your thunk to see what the fetch mock is returning to it.Cory Danielson
@Cory - I see what you're saying but I'm still getting the same results. I updated my code portion above and added a section to show the errorreactFullStackDeveloper
Did you add any console logging in the .then() of getOddGroups? What are you seeing there? It will probably be simpler to mock axios itself, rather than fetch.Cory Danielson
THanks @CoryDanielson I'm looking at axios mock adapter now. I'm confused by your request for the console.log statement. As far as I can see you can not return a console statement from within a test? If I log out result from the actual action, it does give me results.datareactFullStackDeveloper

1 Answers

0
votes

Here is the solution without axios-mock-adapter, don't add too many things in your code, keep it simple. You can mock axios module manually by yourself, look at below code:

actionCreators.ts:

import axios from 'axios';

export const getOddGroups = () => {
  return dispatch => {
    return axios
      .get('/api/tables/oddgroups')
      .then(results => {
        dispatch({ type: 'GET_ODD_GROUPS', payload: results.data });
      })
      .catch(err => {
        dispatch({ type: 'GET_ERRORS', payload: err.response.message });
      });
  };
};

actionCreators.spec.ts:

import { getOddGroups } from './actionCreators';
import createMockStore from 'redux-mock-store';
import thunk, { ThunkDispatch } from 'redux-thunk';
import axios from 'axios';
import { AnyAction } from 'redux';

const middlewares = [thunk];
const mockStore = createMockStore<any, ThunkDispatch<any, any, AnyAction>>(middlewares);

jest.mock('axios', () => {
  return {
    get: jest.fn()
  };
});

describe('actionCreators', () => {
  describe('#getOddGroups', () => {
    let store;
    beforeEach(() => {
      const initialState = {};
      store = mockStore(initialState);
    });
    it('should get odd groups correctly', () => {
      const mockedResponse = { data: 'mocked data' };
      (axios.get as jest.MockedFunction<typeof axios.get>).mockResolvedValueOnce(mockedResponse);
      const expectedActions = [{ type: 'GET_ODD_GROUPS', payload: mockedResponse.data }];
      return store.dispatch(getOddGroups()).then(() => {
        expect(store.getActions()).toEqual(expectedActions);
        expect(axios.get).toBeCalledWith('/api/tables/oddgroups');
      });
    });

    it('should get odd groups error', () => {
      const mockedError = {
        response: {
          message: 'some error'
        }
      };
      (axios.get as jest.MockedFunction<typeof axios.get>).mockRejectedValueOnce(mockedError);
      const expectedActions = [{ type: 'GET_ERRORS', payload: mockedError.response.message }];
      return store.dispatch(getOddGroups()).then(() => {
        expect(store.getActions()).toEqual(expectedActions);
        expect(axios.get).toBeCalledWith('/api/tables/oddgroups');
      });
    });
  });
});

Unit test result with 100% coverage:

 PASS  src/stackoverflow/57730153/actionCreators.spec.ts
  actionCreators
    #getOddGroups
      ✓ should get odd groups correctly (5ms)
      ✓ should get odd groups error (2ms)

-------------------|----------|----------|----------|----------|-------------------|
File               |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
-------------------|----------|----------|----------|----------|-------------------|
All files          |      100 |      100 |      100 |      100 |                   |
 actionCreators.ts |      100 |      100 |      100 |      100 |                   |
-------------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        2.934s, estimated 4s

Here is the completed demo: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/57730153