Gabe
Gabe

Reputation: 6347

ReactJs - test multiple calls in redux-saga with expectSaga

I'm using expectSaga ('redux-saga-test-plan') to test one of my sagas and I'm wondering how to test multiple calls made within the same saga.

Sagas.js

export function* fetchSomething(arg){
  const response = yield call(executeFetch, arg);
  if(response.status === 200){
    // trigger success action
  } else if (response.status >= 400){
    const errResp = yield response.json();
    const errorCode = yield call(sharedUtilToExtractErrors, errResp);
    yield put(
      { type: 'FETCH_FAILED', errorMessage: UI_ERR_MSG, errorCode }
    );
  }
}

Unit test

import { expectSaga } from 'redux-saga-test-plan';

describe('fetchSomething', () => {

   // positive paths

   // ..

   // negative paths

   it('fetches something and with status code 400 triggers FETCH_FAILED with error message and extracted error code', () => {
     const serverError = { message: 'BANG BANG BABY!' };
     const koResponse = new Response(
       JSON.stringify(serverError),
       { status: 400, headers: { 'Content-type': 'application/json' } }
     );

     return expectSaga(fetchSomething)
        .provide(
          {
            call: () => koResponse,
            call: () => serverError.message,
          }
        )
        .put({
           type: 'FETCH_FAILED', errorMessage: UI_ERR_MSG, serverError.message
        })
        .run();
    })
})

Clearly having the "call" attribute twice in the same object passed in to provide() doesn't work but also calling provide() twice doesn't do the trick. Any suggestions?

Thanks

Upvotes: 1

Views: 3065

Answers (1)

Gabe
Gabe

Reputation: 6347

This is how you can provide multiple calls according to the documentation:

.provide([ // this external array is actually optional
  [call(executeFetch, arg), koResponse],
  [call(sharedUtilToExtractErrors, serverError), serverError.message],
])

or if you're lazy and don't want to specify the arguments:

import * as matchers from 'redux-saga-test-plan/matchers';

.provide(
 [matchers.call.fn(executeFetch), koResponse],
 [matchers.call.fn(sharedUtilToExtractErrrors), serverError.message],
)

Neither of these two worked for me though as for some reason it was not mocking out the dependencies and still calling them caused errors.

I solved using a dynamic provider:

.provide({
  // select(effect, next) { return 'something-for-a-selector' },
  call(effect) {
    switch(effect.fn.constructor.name) {
      case executeFetch.constructor.name: return koResponse;
      case sharedUtilToExtractErrors.constructor.name: return serverError.message;
      default: throw new Error('Unknown function called in test');
    }
  }
})

Upvotes: 3

Related Questions