Easiest way to test asynchronous redux sagas with jest

Use case for saga

kamlesh tajpuri
Webtips
3 min readJul 14, 2018

--

Each time a request to get the list of movies is triggered, toggle the application state to loading and fetch the list of movies from server asynchronously. Once the request is completed, toggle the application state to loaded and update the application state with fetched movies.

Let us create our helper function first which will fetch the list of movies. We are using async await feature of ES6 to call our api and return our list of movies wrapped in a promise. Using ES6 async function we can write async code in a much more declarative way. It will wrap the returned object in a promise if you do not explicitly return a promise.

Now let us create our saga.

Take a moment and match it with the use case we have. It should be self explanatory. Whenever an action with type ‘MOVIE_FETCH_REQUESTED’ is triggered, fetchMoviesSaga is called. It will first emit ‘TOGGLE_LOADING’ action with payload true, then call our api and fetch the list of movies asynchronously. After the async call is successful, it will emit ‘TOGGLE_LOADING’ action with payload false and then emit ‘LOAD_INITIAL_MOVIES’ action which will update the state of the component (not covered here). In case there is an error in the api call it will go into the catch block, then emit ‘TOGGLE_LOADING’ action with payload false and trigger the error action ‘ITEM_HAS_ERRORED’.

Now let us write the test for the saga using jest.

Notice the second expectation of first test expect(generator.next().value)
.toEqual(call(fetchMoviesApi))
. We are just checking that the next value of generator is a call to our api. We are not testing the actual results of the call and that is fine because in unit testing we are only concerned that our saga is working as expected and not if our service is returning data correctly. We can write in a separate unit test for that service. That is why our last expectation of first test is that the payload will be undefined :).

Another interesting case is the third expectation of second test. Here we are testing the catch block of our saga. To make our saga throw an exception, we call generator.throw() instead of generator.next(). The value of this throw will be the first yield inside our catch block, which is triggering ‘TOGGLE_LOADING’ action. We are also sending another error action which lets our reducer know that an error has occurred and we can handle it in our state and UI accordingly.

Test results

Congratulations! We just tested our saga with 100% code coverage. Did you notice that we didn’t use any other testing helper libraries or mocks for this? Testing sagas in pure jest.

--

--

kamlesh tajpuri
Webtips

Engineering Manager — React . JS . Node . Perf . People