【问题标题】:Testing fetch action in react/redux app在 react/redux 应用程序中测试 fetch 操作
【发布时间】:2018-08-06 06:28:54
【问题描述】:

我从单元测试和 Jest 开始。我想要的是在从数据库中获取一些资源后测试操作的响应。

这是操作代码:

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

此代码正在从以下位置获取数据:

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

这是测试:

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

});

这是运行测试的结果:

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)

在此示例的 redux 文档 (https://redux.js.org/recipes/writing-tests) 中,他们收到的结果仅包含执行的操作类型,但我得到的是真实数据和数组内的操作。

所以我不确定是代码错了,还是测试错了,或者两者都有!

在此先感谢,非常感谢任何帮助!

【问题讨论】:

  • 如果将window.fetch 更改为global.fetch 会怎样?
  • 嗨@LanceHarper!感谢您的答复。也是一样,我也在api文件中使用import fetch from 'isomorphic-fetch';
  • 您使用的是setupJest.js 文件吗?配置setupJest.js 文件后,您可以分配global.fetch = require('jest-fetch-mock')。然后在您的测试中,您可以使用fetch.mockResponse(JSON.stringify({ ... }) 分配预期的响应
  • 谢谢@LanceHarper 我没有setupJest.js 文件,我应该把它放在哪里?只是在我的测试中创建和导入?我正在寻找一个例子,但我发现的那些看起来非常先进,可以满足我的需要

标签: reactjs redux fetch jestjs jest-fetch-mock


【解决方案1】:

您使用此单元测试进行了太多测试。我看到您正在使用看起来像这样的 thunk,因此您可以将 fetch 作为模块传递给 thunk 并执行类似的操作。我用了茉莉花,但基本上是一样的。你不想在这里模拟你的商店只是动作和调度。单元测试的重点应该是测试异步操作,而不是测试从 db 或 redux 存储交互中获取真实数据,以便您可以存根所有这些。

作为参考,configureStore 看起来像这样...

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

还有测试用例……

  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!感谢您的回复,正如我所说,我是 Jest 和单元测试的新手,但这看起来像是我正在尝试做的事情。你能告诉我Q.resolve(person) 行是什么意思吗? Q 是一个节点包吗?或者它是一个查询......?我在那里迷路了。再次感谢!
  • Q 是一个承诺库,我完全掩盖了这一点。它的工作原理与普通承诺相同。
猜你喜欢
  • 1970-01-01
  • 2023-03-10
  • 1970-01-01
  • 2018-01-21
  • 2016-02-27
  • 1970-01-01
  • 2020-10-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多