-2

I am trying to follow this tutorial but i don't fully understand how to create it. The way i have done actions i think is different to how the examples are shown. from a component i call actions using this.props.actionName but the examples uses dispatch. The following is what one of my action file looks like

app/actions/admin.js

import axios from 'axios';
export const FETCH_CONTENT_VIDEO_LIST = 'fetch_content_video_list';
export function fetchContentVideoList(page, size, where, sort) {
    const request = axios.get(`/api/user/get/content/management/method/video/list/format/json?quiet=1&page=` + page + `&size=` + size + `&where=` + JSON.stringify(where) + `&sort=` + sort);
    return {
        type: FETCH_CONTENT_VIDEO_LIST,
        payload: request
    };
}

app/reducers/reducer_content.js

import _ from 'lodash';
import { FETCH_CONTENTS } from '../actions';
import { 
    FETCH_APPSERVICES, 
    FETCH_APPSERVICES_COUNT,    
    FETCH_OSTYPES,
    FETCH_OSTYPES_COUNT,
    FETCH_ENVTYPES,
    FETCH_ENVTYPES_COUNT,
    FETCH_LOCATIONZONES,
    FETCH_LOCATIONZONES_COUNT,
    UPLOAD_DOCUMENT_SUCCESS,
    UPLOAD_DOCUMENT_FAIL,
} from '../actions/content';
import { FETCH_CONTENT_VIDEO_LIST, FETCH_CONTENT_VIDEO_LIST_COUNT, UPDATE_CONTENT_VIDEO_LIST } from '../actions/admin';

export default function(state = {}, action) {
    switch (action.type) {
        case FETCH_CONTENTS:
            if ( typeof action.payload.data !== 'object'){
                return Object.assign({}, state, {
                    error: {
                        code: "AUTHENTICATION",
                        message: 'Your session has expired',
                        action
                    }
                 });
            }else if (action.payload.data.header.error) {
                return Object.assign({}, state, {
                    error: {
                        code: "INVALID ACTION",
                        message: action.payload.data.header.message,
                        action
                    }
                 });

           } else {
               return _.mapKeys(action.payload.data.body.recordset.record, 'target');
           }


    case FETCH_APPSERVICES:
    case FETCH_APPSERVICES_COUNT:
    case FETCH_LOCATIONZONES:
    case FETCH_LOCATIONZONES_COUNT:
    case FETCH_OSTYPES:
    case FETCH_OSTYPES_COUNT:
    case FETCH_ENVTYPES:
    case FETCH_ENVTYPES_COUNT:  
    case FETCH_CONTENT_VIDEO_LIST:
    case FETCH_CONTENT_VIDEO_LIST_COUNT:
    case UPDATE_CONTENT_VIDEO_LIST: 
        if ( typeof action.payload.data !== 'object'){
            return Object.assign({}, state, {
                error: {
                    code: "AUTHENTICATION",
                    message: 'Your session has expired',
                    action
                }
             });
        }else if (action.payload.data.header.error) {
            return Object.assign({}, state, {
                error: {
                    code: "INVALID ACTION",
                    message: action.payload.data.header.message,
                    action
                }
             });

       } else {
           return action.payload.data.body.recordset.record;
       }

    case UPLOAD_DOCUMENT_SUCCESS:
    case UPLOAD_DOCUMENT_FAIL:
        return action;
    default:
        return state;
    }
}

my test looks like the following but it fails. I am not using redux-thunk.

test/jest/actions/admin.test.js

    import configureMockStore from 'redux-mock-store'
import * as actions from '../../../app/actions/admin'
import * as types from '../../../app/reducers/reducer_content'
import fetchMock from 'fetch-mock'
import expect from 'expect' // You can use any testing library

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

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

  it('FETCH_CONTENT_VIDEO_LIST', () => {
    fetchMock
      .getOnce('/contentvideolist', { body: { todos: ['do something'] }, headers: { 'content-type': 'application/json' } })

console.log(types.FETCH_CONTENT_VIDEO_LIST);
    const expectedActions = [
        { type: types.FETCH_CONTENT_VIDEO_LIST }
    ]
    const store = mockStore({ todos: [] })
      console.log(store);
console.log(actions);
    return store.dispatch(actions.fetchContentVideoList()).then(() => {
      // return of async actions
        console.log(store.getActions());
      expect(store.getActions()).toEqual(expectedActions)
    })
  })
})

It fails on here.

 FAIL  tests/jest/actions/admin.test.js
  async actions
    ✕ FETCH_CONTENT_VIDEO_LIST (14ms)

  ● async actions › FETCH_CONTENT_VIDEO_LIST

    TypeError: store.dispatch(...).then is not a function

      25 |     const store = mockStore({ todos: [] })
      26 |
    > 27 |     return store.dispatch(actions.fetchContentVideoList()).then(() => {
      28 |       // return of async actions
      29 |      console.log(store.getActions());
      30 |       expect(store.getActions()).toEqual(expectedActions)

      at Object.<anonymous> (tests/jest/actions/admin.test.js:27:60)

UPDATE

here is new error

FAIL  ../actions/admin.test.js
  async actions
    ✕ FETCH_CONTENT_VIDEO_LIST (33ms)

  ● async actions › FETCH_CONTENT_VIDEO_LIST

    TypeError: store.dispatch(...).then is not a function

      25 |     const store = mockStore({ todos: [] })
      26 | console.log(actions);
    > 27 |     return store.dispatch(actions.fetchContentVideoList()).then(() => {
      28 |       // return of async actions
      29 |      console.log(store.getActions());
      30 |       expect(store.getActions()).toEqual(expectedActions)

      at Object.<anonymous> (tests/jest/actions/admin.test.js:27:60)

  console.log ../actions/admin.test.js:26
    { FETCH_CONTENT_VIDEO_LIST: 'fetch_content_video_list',
      FETCH_CONTENT_VIDEO_LIST_COUNT: 'fetch_content_video_list_count',
      UPDATE_CONTENT_VIDEO_LIST: 'update_content_video_list',
      fetchContentVideoList: [Function: fetchContentVideoList],
      fetchContentVideoListCount: [Function: fetchContentVideoListCount],
      updateContentVideoList: [Function: updateContentVideoList] }

update2

import configureMockStore from 'redux-mock-store'
//import fetchMock from 'fetch-mock'
import expect from 'expect' // You can use any testing library
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import * as actions from '../../../app/actions/admin'
import * as types from '../../../app/reducers/reducer_content'


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

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

  it('FETCH_CONTENT_VIDEO_LIST', () => {
    let mock = new MockAdapter(axios);
      const data = { response: true };
      mock.onGet('/contentvideolist').reply(200, data);


    console.log(types);
    const expectedActions = [
        { type: types.FETCH_CONTENT_VIDEO_LIST }
    ]
    const store = mockStore({ todos: [] })
    console.log(actions);
    store.dispatch(actions.fetchContentVideoList());
    expect(store.getActions()).toEqual(expectedActions);
  });
});

the types just shows as a [function] so i cannot do types.FETCH_CONTENT_VIDEO_LIST. it displays as undefined

result

     ● async actions › FETCH_CONTENT_VIDEO_LIST

        expect(received).toEqual(expected)

        Expected value to equal:
          [{"type": undefined}]
        Received:
          [{"payload": {}, "type": "fetch_content_video_list"}]

        Difference:

        - Expected
        + Received

          Array [
            Object {
        -     "type": undefined,
        +     "payload": Promise {},
        +     "type": "fetch_content_video_list",
            },
          ]

          26 |     console.log(actions);
          27 |     store.dispatch(actions.fetchContentVideoList());
        > 28 |     expect(store.getActions()).toEqual(expectedActions);
          29 |   });
          30 | });
          31 |

          at Object.<anonymous> (tests/jest/actions/admin.test.js:28:32)

      console.log tests/jest/actions/admin.test.js:21
        { default: [Function] }

      console.log tests/jest/actions/admin.test.js:26
        { FETCH_CONTENT_VIDEO_LIST: 'fetch_content_video_list',
          FETCH_CONTENT_VIDEO_LIST_COUNT: 'fetch_content_video_list_count',
          UPDATE_CONTENT_VIDEO_LIST: 'update_content_video_list',
          fetchContentVideoList: 

[Function: fetchContentVideoList],
      fetchContentVideoListCount: [Function: fetchContentVideoListCount],
      updateContentVideoList: [Function: updateContentVideoList] }

update 3

i tried to copy this https://www.leighhalliday.com/mocking-axios-in-jest-testing-async-functions

but its failing on the type recieved back

import * as actions from '../../../app/actions/admin'
//import * as types from '../../../app/reducers/reducer_content'

const mockAxios = {
  get: jest.fn(() => Promise.resolve({ data: {} }))
};

it("fetches data for FETCH_CONTENT_VIDEO_LIST", async () => {
  // setup
  mockAxios.get.mockImplementationOnce(() =>
    Promise.resolve({
      data: { results: {"payload": {}, "type": actions.FETCH_CONTENT_VIDEO_LIST} }
    })
  );

  // work
  const contentVideoList = await actions.fetchContentVideoList(1,1,{},'id');

  // expect
  const expectedActions = {"payload": {}, "type": actions.FETCH_CONTENT_VIDEO_LIST}

  expect(contentVideoList).toEqual(expectedActions);
  expect(mockAxios.get).toHaveBeenCalledTimes(1);
  expect(mockAxios.get).toHaveBeenCalledWith(
    "/api/user/get/content/management/method/video/list/format/json",
    {
      params: {
        client_id: process.env.REACT_APP_UNSPLASH_TOKEN,
        query: "cats"
      }
    }
  );
});

result

● fetches data for FETCH_CONTENT_VIDEO_LIST

    expect(received).toEqual(expected)

    Expected value to equal:
      {"payload": {}, "type": "fetch_content_video_list"}
    Received:
      {"payload": {}, "type": "fetch_content_video_list"}

    Difference:

    - Expected
    + Received

      Object {
    -   "payload": Object {},
    +   "payload": Promise {},
        "type": "fetch_content_video_list",
      }

      20 |   const expectedActions = {"payload": {}, "type": actions.FETCH_CONTENT_VIDEO_LIST}
      21 |
    > 22 |   expect(contentVideoList).toEqual(expectedActions);
      23 |   expect(mockAxios.get).toHaveBeenCalledTimes(1);
      24 |   expect(mockAxios.get).toHaveBeenCalledWith(
      25 |     "https:///api/user/get/content/management/method/video/list/format/json",

      at _callee$ (tests/jest/actions/admin.test.js:22:28)
      at tryCatch (node_modules/regenerator-runtime/runtime.js:62:40)
      at Generator.invoke [as _invoke] (node_modules/regenerator-runtime/runtime.js:296:22)
      at Generator.prototype.(anonymous function) [as next] (node_modules/regenerator-runtime/runtime.js:114:21)
      at step (tests/jest/actions/admin.test.js:9:191)
      at tests/jest/actions/admin.test.js:9:361

update 4

import configureMockStore from 'redux-mock-store'
//import fetchMock from 'fetch-mock'
import expect from 'expect' // You can use any testing library
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import * as actions from '../../../app/actions/admin'
import * as types from '../../../app/reducers/reducer_content'


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

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

  it('FETCH_CONTENT_VIDEO_LIST', () => {
    let mock = new MockAdapter(axios);
      const data = { response: true };
      mock.onGet('/contentvideolist').reply(200, data);


    console.log(types);
    const expectedActions = [{"payload": {}, "type": actions.FETCH_CONTENT_VIDEO_LIST}]
    const store = mockStore({ fetch_content_video_list: [] })
    console.log(actions);
    store.dispatch(actions.fetchContentVideoList());
    expect(store.getActions()).toEqual(expectedActions);
  });

});

result 4

● async actions › FETCH_CONTENT_VIDEO_LIST

    expect(received).toEqual(expected)

    Expected value to equal:
      [{"payload": {}, "type": "fetch_content_video_list"}]
    Received:
      [{"payload": {}, "type": "fetch_content_video_list"}]

    Difference:

    - Expected
    + Received

      Array [
        Object {
    -     "payload": Object {},
    +     "payload": Promise {},
          "type": "fetch_content_video_list",
        },
      ]

      24 |     console.log(actions);
      25 |     store.dispatch(actions.fetchContentVideoList());
    > 26 |     expect(store.getActions()).toEqual(expectedActions);
      27 |   });
      28 | });
      29 |

      at Object.<anonymous> (tests/jest/actions/admin.test.js:26:32)

  console.log tests/jest/actions/admin.test.js:21


       { default: [Function] }

      console.log tests/jest/actions/admin.test.js:24
        { FETCH_CONTENT_VIDEO_LIST: 'fetch_content_video_list',
          FETCH_CONTENT_VIDEO_LIST_COUNT: 'fetch_content_video_list_count',
          UPDATE_CONTENT_VIDEO_LIST: 'update_content_video_list',
          fetchCo

ntentVideoList: [Function: fetchContentVideoList],
      fetchContentVideoListCount: [Function: fetchContentVideoListCount],
      updateContentVideoList: [Function: updateContentVideoList] }
skyboyer
  • 22,209
  • 7
  • 57
  • 64
shorif2000
  • 2,582
  • 12
  • 65
  • 137

1 Answers1

2

store.dispatch does not return a promise, but returns the action that was dispatched (see dispatch docs), so it is not possible to call then on the result.

Instead, do something like this, which dispatches the action created by the function, then checks that the correct action was dispatched:

store.dispatch(actions.fetchContentVideoList());
expect(store.getActions()).toEqual(expectedActions);
A Jar of Clay
  • 5,622
  • 6
  • 25
  • 39
  • how comes in the tutorial it uses `.then(()` on the action? – shorif2000 Aug 02 '18 at 12:36
  • do i need `fetchMock .getOnce('/contentvideolist', { body: { todos: ['do something'] }, headers: { 'content-type': 'application/json' } })` ? i don't know what it does – shorif2000 Aug 02 '18 at 12:37
  • In the tutorial, their action creator returns a promise, and `dispatch` returns this promise, which is thenable. Your `fetchContentVideoList` doesn't return a promise, so you can't call `.then` on the dispatched action. – A Jar of Clay Aug 02 '18 at 12:57
  • `fetch-mock` is used to mock out `fetch` REST calls. I'm not sure whether it would work for `axios` too, which is an alternative to `fetch`. This mocking is needed because otherwise your test will make an actual request, which is not what you want. This might help you: https://stackoverflow.com/questions/45016033/how-do-i-test-axios-in-jest – A Jar of Clay Aug 02 '18 at 13:06
  • see my update2. i have also outputed the data using console log – shorif2000 Aug 02 '18 at 13:59
  • i have added update 3 using a different way to get it to work. – shorif2000 Aug 02 '18 at 14:34
  • i rewrote it again and now it seems its expecting to match an object but i receive a Promise – shorif2000 Aug 02 '18 at 14:53
  • This is an unrelated issue to the original one. Perhaps it would be better to ask another question rather than adding lots of updates? – A Jar of Clay Aug 02 '18 at 14:55
  • The `fetchContentVideoList` is incorrect, because you are not waiting for the promise to resolve before returning the action. – A Jar of Clay Aug 02 '18 at 14:57
  • i have created a new question https://stackoverflow.com/questions/51656720/how-to-match-a-returned-promise-in-using-jest-with-redux-action – shorif2000 Aug 02 '18 at 14:59