Redux runSaga(单元测试)错误在本地变量上引发未定义。如何使用Jest或Sinon模拟本地变量?

问题描述 投票:6回答:2

在我的redux saga生成器函数上运行runSaga时,我的窗口变量被抛出为未定义,但是我的测试文件正在通过,有没有办法模拟窗口变量?

下面是我的redux saga生成器函数

import api from 'my-api';
    
const getSuburb = () => window.userCookies.selectedSuburb;

function* saga(payload) {
 const action = yield take('REQUEST_LIBRARY');
 const selectedSuburb = yield call(getSuburb);
 const getStateLibraries = yield call(api.getLibraries, selectedSuburb, action.userId);
 yield put(loadLibrary(getStateLibraries)
}

运行上述代码后,我收到了有关郊区的图书馆列表,我还有另一个州保存郊区信息,我可以在其中使用select来检索它。代码工作正常

使用RunSaga测试Redux传奇的单元测试用例

const recordSaga = async function (sagaHandler, initalAction) {
  const dispatchedActions = [];
  const fakeStore = {
    getState: () => (initialState),
    dispatch: action => dispatchedActions.push(action),
  };
  await runSaga(
    fakeStore,
    sagaHandler,
    initalAction,
  ).done;
  return dispatchedActions;
};

describe('Run Saga', () => {
 it('should dispatch action libraries', async() => {
  const dispatched = await recordSaga(saga, { user_id:2 });
  expect(dispatched).toContainEqual(loadLibrarySuccess(someProfile));
 }

运行上述命令时,我未定义selectedSuburb,因为未定义window.userCookies.totalSuburbs,有没有更好的方法来模拟getSuburb函数?

jestjs sinon redux-saga
2个回答
0
投票

好吧,我已经修改了如下代码以确保我的测试用例正在运行,但是我仍在寻找模拟局部变量的完美解决方案

function* saga(payload) {
 const action = yield take('REQUEST_LIBRARY');
 const selectedSuburb = window.userCookies.selectedSuburb
 const getStateLibraries = yield call(api.getLibraries, selectedSuburb, action.userId);
 yield put(loadLibrary(getStateLibraries)
}

我的测试用例看起来像

const recordSaga = async function (sagaHandler, initalAction) {
  const dispatchedActions = [];
  const fakeStore = {
    getState: () => (initialState),
    dispatch: action => dispatchedActions.push(action),
  };
  await runSaga(
    fakeStore,
    sagaHandler,
    initalAction,
  ).done;
  return dispatchedActions;
};

describe('Run Saga', () => {
 it('should dispatch action libraries', async() => {
 const callback = sinon.stub(this, 'userCookies.selectedSuburb');
   callback.onCall(0).returns('suburb_name');
  const dispatched = await recordSaga(saga, { user_id:2 });
  expect(dispatched).toContainEqual(loadLibrarySuccess(someProfile));
 }

0
投票

我建议查看Redux-Saga文档如何建议sagas testing。在单元测试中,仅应测试函数saga的逻辑,而不应调用任何第三方函数。而yield关键字对此有很大帮助。

基本上,我们测试生成器函数(在此示例中为函数saga)将在每次调用时返回正确的对象。因此,saga功能的单元测试将看起来像(未经测试,仅是示例)

describe('Run Saga', () => {
  it('should dispatch action libraries', () => {
    const gen = saga()  // As saga is generator function it will return generator object
    expect(gen.next().value).toStrictEqual(take('REQUEST_LIBRARY'))
    expect(gen.next({ userId: 'user1' } /* here can be action returned by take('REQUEST_LIBRARY') in saga */).value).toStrictEqual(call(getSuburb))
    expect(gen.next({ suburb: 'central suburb' } /* here can be selectedSuburb returned by call(getSuburb) in saga */).value).toStrictEqual(call(api.getLibraries, { suburb: 'central suburb' }, 'user1'))
    // The values of selectedSuburb and action.userId are from calls to gen.next()
    expect(gen.next({ libraries: ['lib 1', 'lib 2'] } }).value).toStrictEqual(put(loadLibrary({ libraries: ['lib 1', 'lib 2'] }))
    expect(gen.next().done).toBe(true); // Saga is finished
  }
})

因此,单元测试仅检查saga本身,而不会在执行时调用saga调用的任何函数。因此,使用sagas时根本不需要模拟任何东西。这证实了单元测试的思想,即仅测试单个代码(功能)单元。

您可能会注意到,从yield返回的值应传递给next()函数,但应在next行上。这就是发电机的工作方式。

© www.soinside.com 2019 - 2024. All rights reserved.