Redux-saga: yield select in test is undefined

Created on 6 Aug 2016  路  3Comments  路  Source: redux-saga/redux-saga

When I run my saga via tests, it doesn't find the state when using yield select. When I run it through the application, it works fine.

Saga:

export function *quitTimer() {
  const pomodoro = yield select(selectors.pomodoroSelector);
  const state = yield select();
  console.log(state); --> UNDEFINED
  console.log(pomodoro); --> UNDEFINED
  // if (pomodoro) {
  //   pomodoro.timer.quit();
  // }
  yield call(pomodoro.timer.quit)
  yield put(actions.onPomodoroQuit(pomodoro));
}

Test:

describe('Home sagas', () => {
  it('quitTimer Saga test', function (done) {
    const generator = sagas.quitTimer(getState);

    let next = generator.next();
    expect(next.value).toEqual(select(selectors.pomodoroSelector));

    next = generator.next();
    expect(next.value).toEqual(select());

    // TODO: figure out how to test:
    next = generator.next();
    expect(next.value).toEqual(select(pomodoro.timer.quit));

    next = generator.next();
    expect(next.value).toEqual(put(actions.onPomodoroQuit(pomodoro)));

    done();
  });
});

For whatever reason, pomodoro is coming back undefined from state only on the tests, so I'm getting the error:

 1) Home sagas quitTimer Saga test:
     TypeError: Cannot read property 'timer' of undefined

I can, of course, wrap the call in an if statement, but I think that should be unnecessary.

I've looked over all the examples, and I can't seem to figure out what I'm doing wrong.

Thanks!

Most helpful comment

@jrvidal - Yeah, I definitely saw that the getState was unused... I left mine in there (for no particular reason).

I didn't realize that I needed to send generator.next(THE_EXPECTED_VALUE).

Here's what I ended up with:
saga

export function *quitTimer() {
  const pomodoro = yield select(selectors.pomodoroSelector);
  yield call(pomodoro.timer.quit)
  yield put(actions.onPomodoroQuit(pomodoro));
}

test:

it('quitTimer Saga test', function (done) {
  const generator = sagas.quitTimer();

  let next = generator.next();
  expect(next.value).toEqual(select(selectors.pomodoroSelector));

  next = generator.next(getState().pomodoro);
  expect(next.value).toEqual(call(pomodoro.timer.quit));

  next = generator.next();
  expect(next.value).toEqual(put(actions.onPomodoroQuit(pomodoro)));

  done();
});

And it works. Thanks @jrvidal !

All 3 comments

Effects are purely declarative, so there is not way for them to have "effect" if you don't run a saga connecting it to a store. In this kind of test, you need to manually mock the returned values. Try:

  it('quitTimer Saga test', function (done) {
    // This `getState` does not seem to serve any purpose
    const generator = sagas.quitTimer(getState);

    let next = generator.next();
    expect(next.value).toEqual(select(selectors.pomodoroSelector));

    next = generator.next(yourExpectedResponseForTheSelectEffect);
    // ...

I'm wondering if you got confused by this example that also uses an ignored getState argument.

@jrvidal - Yeah, I definitely saw that the getState was unused... I left mine in there (for no particular reason).

I didn't realize that I needed to send generator.next(THE_EXPECTED_VALUE).

Here's what I ended up with:
saga

export function *quitTimer() {
  const pomodoro = yield select(selectors.pomodoroSelector);
  yield call(pomodoro.timer.quit)
  yield put(actions.onPomodoroQuit(pomodoro));
}

test:

it('quitTimer Saga test', function (done) {
  const generator = sagas.quitTimer();

  let next = generator.next();
  expect(next.value).toEqual(select(selectors.pomodoroSelector));

  next = generator.next(getState().pomodoro);
  expect(next.value).toEqual(call(pomodoro.timer.quit));

  next = generator.next();
  expect(next.value).toEqual(put(actions.onPomodoroQuit(pomodoro)));

  done();
});

And it works. Thanks @jrvidal !

We can do like this

Saga Generator function

export function* getAuthToken(action) {
try {
const authToken = yield select(makeSelectAuthToken());
} catch (errObj) {
}
}

Test Case

import { runSaga } from 'redux-saga'

const fakeStore = {
getState: () => ({ authReducer: { auth: 'test' } }),
dispatch: (action) => dispatchedActions.push(action)
}

  await runSaga(fakeStore, getAuthToken, {
    payload: {}
  }).done;

expect case you can write here below this

Was this page helpful?
0 / 5 - 0 ratings

Related issues

anthonychung14 picture anthonychung14  路  3Comments

Beingbook picture Beingbook  路  3Comments

oliversisson picture oliversisson  路  3Comments

mpyw picture mpyw  路  3Comments

dalexand picture dalexand  路  3Comments