dougajmcdonald
dougajmcdonald

Reputation: 20047

How to test `call(fn, params)` with redux-saga

I'm seeing some strange results when testing what I thought were simple saga's with redux-saga. Take this saga for example:

export function* initSaga() {

  yield call(window.clearTimeout, runner)

  yield call(PubSub.publishSync, 'RESET_CORE')

}

My mental model of the test reads, first check it called window.clearTimeout with a parameter which matches the value of runner then test it called the publishSync method of PubSub with the value 'RESET_CORE'

My test for the first part reads:

describe('when testing the init saga', () => {

  const saga = initSaga()
  const runner = null 

  it('first clears the draw loop for the canvas', () => {

    const result = saga.next().value
    const expected = call(window.clearInterval, runner)

    console.log(result)
    console.log(expected)

    expect(result).to.deep.equal(expected)

  })
})

What's frustrating is that the error message reads:

AssertionError: expected { Object (@@redux-saga/IO, CALL) } to deeply equal { Object (@@redux-saga/IO, CALL) }

And my console logs read:

 console.log src\tests\sagas\simulatorSaga.test.js:25
    { '@@redux-saga/IO': true,
      CALL:
       { context: null,
         fn: [Function: bound stopTimer],
         args: [ null ] } }

  console.log src\tests\sagas\simulatorSaga.test.js:26
    { '@@redux-saga/IO': true,
      CALL:
       { context: null,
         fn: [Function: bound stopTimer],
         args: [ null ] } }

Which to me look identical. I'm presuming there is something simple I'm missing here, some kind of deep equal object reference kind of stuff but I'm not sure bearing in mind the saga and test how I could make this any more cut down.

I'm aware that the whole window + pubsub isn't exactly how saga's are meant to be used, but the app is interfacing with a canvas for running a simulation so we kind of have to do it that way.

Upvotes: 0

Views: 1149

Answers (1)

Krasimir
Krasimir

Reputation: 13529

I know that that's what the official docs suggest but I don't like such testing. It looks a little bit like testing if redux-saga does its job. What you are really interested in is if clearInterval and publishSync are executed with correct parameters. You don't care what the generator returns. So here's what I'm suggesting:

const executeSaga = function (saga) {
  var result = saga.next();

  while(!result.done) {
    result = saga.next();
  }
}

describe('when testing the init saga', () => {

  const runner = null;

  it('first clears the draw loop for the canvas', () => {
    sinon.stub(window, 'clearInterval');
    sinon.stub(PubSub, 'publishSync');
    executeSaga(initSaga());

    expect(window.clearInterval)
      .to.be.calledOnce
      .and.to.be.calledWith(runner);

    expect(PubSub.publishSync)
      .to.be.calledOnce
      .and.to.be.calledWith('RESET_CORE');

    window.clearInterval.restore();
    PubSub.publishSync.restore();
  })
})

It involves sinonjs. We are stubbing both functions, run the whole saga and then expect the stubs.

Upvotes: 1

Related Questions