代码之家  ›  专栏  ›  技术社区  ›  shorif2000

如何测试动作创建者发送正确的动作?

  •  -1
  • shorif2000  · 技术社区  · 8 年前

    我正试着跟着这个 tutorial 但我不完全理解如何创造它。我认为我做动作的方式与示例的显示方式不同。从我使用 this.props.actionName 但这些例子 dispatch . 下面是我的一个操作文件

    应用程序/操作/管理.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
        };
    }
    

    应用程序/还原程序/还原程序内容.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;
        }
    }
    

    我的测试看起来如下,但失败了。我不使用 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)
        })
      })
    })
    

    在这里失败了。

     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)
    

    更新

    这是新的错误

    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] }
    

    更新2

    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);
      });
    });
    

    这个 types 只是作为一个 [function] 所以我做不到 types.FETCH_CONTENT_VIDEO_LIST . 它显示为 undefined

    结果

         ● 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] }
    

    更新3

    我试图复制这个 https://www.leighhalliday.com/mocking-axios-in-jest-testing-async-functions

    但它在类型上失败了

    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"
          }
        }
      );
    });
    

    结果

    ● 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
    

    更新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);
      });
    

    });

    结果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] }
    
    1 回复  |  直到 7 年前
        1
  •  2
  •   A Jar of Clay    8 年前

    store.dispatch 不返回承诺,但返回已发送的操作(请参阅 dispatch docs ),所以无法拨打 then 关于结果。

    相反,执行如下操作,即分派由函数创建的操作,然后检查是否分派了正确的操作:

    store.dispatch(actions.fetchContentVideoList());
    expect(store.getActions()).toEqual(expectedActions);