【问题标题】:Redux saga testing using runSaga not updating the state使用 runSaga 的 Redux saga 测试不更新状态
【发布时间】:2021-08-03 18:03:22
【问题描述】:

所以我正在尝试使用 runSaga 函数测试一个 redux saga。这个 saga 调用 API 并通过调用操作将其响应存储在状态中。存储后,我正在使用选择器检索存储的数据并执行一些操作。我能够模拟 API 响应并在 API 返回后调用成功操作。

当我在下一行尝试调用选择器从存储中获取数据时,它似乎是从存储中获取初始状态,而不是 API 成功后的更新状态。

这是我的代码:

// loadSaga.js. This is the saga that I'm testing
export function* loadSaga(request) {
  const endpoint = yield select(ListEndpointSelector);
  const listResponse = yield call(request, endpoint, 'GET', {}, throwIfNot404);
  console.log(listResponse); // Returns the correct mocked API response
  yield put(loadListSuccess(listResponse));
  const { list as newList } = yield select(listSelector);
  console.log(newList); // Returns empty array
  // ... do something with newList but newList is empty
};
// loadSaga.test.js
import { runSaga } from 'redux-saga';
import * as reduxSagaEffects from 'redux-saga/effects';

const mockState = {
    user: {
      list: [],
    },
  },
};
describe('loadSaga', () => {
  it('returns the correct list of users', async () => {
    const dispatchedActions = [];
    const mockUsers = [
    {
     id: 1,
     name: 'abc',
    },
    {
     id: 2,
     name: 'xyz',
    },
    ]

    const mockfn = jest.fn().mockImplementationOnce(() => mockUsers);

    // eslint-disable-next-line
    const m = jest.mock('redux-saga/effects', () => ({
      ...reduxSagaEffects,
      call: mockfn,
    }));
    const { loadSaga } = require('./loadSaga.js');
    runSaga({
      dispatch: (action) => dispatchedActions.push(action),
      getState: () => (mockState),
    }, loadSaga);

    console.log(dispatchedActions);
});

当我控制台 listResponse 时,我从我在 mockFn 中设置的 API 获得了正确的 mockedResponse。但是当我控制台newList 时,它返回[] 这似乎是mockState 中设置的列表。

已调度操作的 console.log 显示正在调度的正确操作,甚至是 loadListSuccess 以及传递给它的模拟 API 响应。

由于yield select(listSelector) 没有返回正确的输出,我无法测试进一步的测试用例。我应该更改哪些内容才能在选择器中检索当前更新的状态?

【问题讨论】:

    标签: reactjs unit-testing redux jestjs redux-saga


    【解决方案1】:

    loadSaga saga 没有连接到 redux 存储。 select(selector, ...args) 只是创建一个效果对象。当redux store使用redux-saga作为中间件时,redux-saga只能获取redux的getState方法,传递给select创建的效果。

    您可以使用runSagagetState() 选项来创建模拟商店。您必须自己维护状态数据的正确性,以确保后续依赖list的逻辑正确执行。

    此外,您不需要模拟redux-sagacall 效果创建者,因为loadSaga 接受request 处理程序,您可以为其创建一个模拟并将其传递给@ 的第三个参数987654336@.

    例如

    loadSaga.ts:

    import { select, call, put } from 'redux-saga/effects';
    
    const listSelector = (state) => {
      console.log('state: ', state);
      return state.user;
    };
    
    export function loadListSuccess(payload) {
      return { type: 'LOAD_LIST_SUCCESS', payload };
    }
    
    export function* loadSaga(request) {
      const listResponse = yield call(request);
      console.log('listResponse: ', listResponse);
      yield put(loadListSuccess(listResponse));
      const { list } = yield select(listSelector);
      console.log('list: ', list);
    }
    

    loadSaga.test.ts:

    import { runSaga } from 'redux-saga';
    import { loadListSuccess, loadSaga } from './loadSaga';
    
    describe('68632358', () => {
      test('should pass', async () => {
        const dispatchedActions: any[] = [];
        const mockUsers = [
          { id: 1, name: 'abc' },
          { id: 2, name: 'xyz' },
        ];
        const mockState = {
          user: {
            list: mockUsers,
          },
        };
        const mRequest = jest.fn().mockResolvedValue(mockUsers);
        await runSaga(
          {
            dispatch: (action) => dispatchedActions.push(action),
            getState: () => mockState,
          },
          loadSaga as any,
          mRequest,
        ).toPromise();
        expect(mRequest).toBeCalled();
        expect(dispatchedActions).toEqual([loadListSuccess(mockUsers)]);
      });
    });
    

    测试结果:

     PASS  src/*/68632358/loadSaga.test.ts
      68632358
        ✓ should pass (21 ms)
    
      console.log
        listResponse:  [ { id: 1, name: 'abc' }, { id: 2, name: 'xyz' } ]
    
          at src/*/68632358/loadSaga.ts:14:11
    
      console.log
        state:  { user: { list: [ [Object], [Object] ] } }
    
          at listSelector (src/*/68632358/loadSaga.ts:4:11)
    
      console.log
        list:  [ { id: 1, name: 'abc' }, { id: 2, name: 'xyz' } ]
    
          at src/*/68632358/loadSaga.ts:17:11
    
    Test Suites: 1 passed, 1 total
    Tests:       1 passed, 1 total
    Snapshots:   0 total
    Time:        1.641 s, estimated 2 s
    

    包版本:"redux-saga": "^1.1.3"

    【讨论】:

    • 感谢您的回复!但我收到TypeError: (0 , _reduxSaga.runSaga)(...).toPromise is not a function 错误。
    • @krishnanspace 它应该可以工作。 runSaga 返回一个具有.toPromise() 方法的Task 对象
    • 看起来我的 runSaga 没有返回 toPromise()。我正在使用"redux-saga": "^0.15.4",
    • @krishnanspace 尝试使用task.done getter。 task.toPromise()方法在v1.0中发布,见github.com/redux-saga/redux-saga/blob/master/…
    • 我使用了 task.done 但选择器仍然返回一个空数组
    最近更新 更多