Redux runSaga (модульное тестирование) выдает ошибку неопределенной локальной переменной. Как мне издеваться над локальной переменной, используя Jest или Sinon? - PullRequest
6 голосов
/ 31 октября 2019

При запуске runSaga на моей функции генератора саги-редукта моя оконная переменная отображается как неопределенная, но мои тестовые файлы передаются. Есть ли способ насмешить переменную окна?

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

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

Ответы [ 2 ]

0 голосов
/ 12 ноября 2019

Рекомендую посмотреть, как документы Redux-Saga предлагают тестирование саг . В модульном тестировании должна проверяться только логика функции 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
  }
})

Таким образом, модульный тест проверяет только саму сагу и не вызывает никаких функций, которые сага вызывает при выполнении. Таким образом, вам не нужно ничего издеваться над сагами. Это подтверждает идею модульного тестирования для тестирования только одной единицы кода (функции) в изоляции.

Как вы могли заметить, значение, возвращаемое из yield, должно быть передано в функцию next(), но в следующей строке. Вот как работает генератор.

0 голосов
/ 08 ноября 2019

Хорошо, я изменил код следующим образом, чтобы убедиться, что мои тестовые случаи выполняются, но я все равно искал бы идеальное решение для имитации локальной переменной

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