Testing Redux-Sagas with a plan.

Why Saga Testing and how?

Redux Saga is a library that aims to make application side effects (i.e. asynchronous things like data fetching and impure things like accessing the browser cache) easier to manage, more efficient to execute, simple to test, and better at handling failures.
— Redux Saga Docs

Today, we use Redux-Saga heavily in our enterprise application. Most of the logic and all of the side-effects are handled by our Sagas. Our Components and Reducers are mostly dumb.

We treat the root Saga like a separate thread in our application. Any action that can lead to a side effect is handled by our saga thread.

Mental Model of how our UI works.

From a testing perspective, the main advantage of this architecture is that all our side effects and logic conditions are in one place i.e Sagas. Testing Sagas alone would give us much greater confidence than testing reducers or components. So, testing Sagas is an imperative for us.

There are two ways to test sagas.

Stepping through each yield in Saga one by one

With this approach, you will step through yield effects one by one. Typically, in the exact order by asserting the effect objects at each step. This is like unit testing your sagas. Though straight forward, this will make your tests brittle. As your tests are tightly coupled with the implementation and also the order of effects. Any refactoring or restructuring will most likely break your tests.

So, we decided to go with the second option.

Testing not how a saga is implemented but only the effects that you’re interested in.

Here you will run the saga to the end, mocking select and call effects and asserting the put effects you are interested in.

This is more towards integration testing. As you are not running your saga effects in isolation one after the other, this type of test should give you more confidence.

The main benefit, however, is your tests are not Brittle. They are not tightly coupled with the implementation. Example: If you re-order your effects or you add one unrelated put effect for a corner case, it should not break your test.

While both of the above testing methods can be written natively, there exist several libraries to make both methods easier. For testing each generator yield step-by-step there is redux-saga-test and redux-saga-testing for option one. redux-saga-tester, redux-saga-test-plan and redux-saga-test-engine for option two. You can also unit test with test plan and tester.
— Redux Saga Docs

We decided to go with Redux-saga-test-plan.

Let’s test a basic saga worker which is written for the requirements below.

If user is not logged in, he should be redirected to login.
If user is logged in and does not have a right to open the Modal, he should be denied access.
If user is logged in and has the right to open the Modal, Modal opens.
Trigger Modal Saga

Our TriggerModal Saga worker when triggered by an action selects the UserId and the ModalRight from the Redux store using the select effect.

If UserId is not falsy, it means user is logged in.

The ModalRight is true, if he has the action right and false if he does not.

We have to test our saga worker in three different redux states where:

1. User is not logged in

2. He is logged in but does not have the right

3. He is logged in and has the right.

Our three Mocked states look like these.

States mocked to test saga.

We intend to test the behaviour not the implementation.

For the above three states we have three cases.

Saga effect testing

For all the three cases, we run the saga and pass the corresponding redux states. We then mock the select effects and assert what the put effects should be.

We are only asserting what comes out of saga in a particular state of application there by testing only the behaviour and not the implementation.

Here, even if you change the order of select effects in the saga or add a new independent effect it will not necessarily break the Test.

Also after .run. you could do .then and assert with even more control on values of effect objects.

For example, in each of the above cases we know only one put should be dispatched. We can assert the same using .then as shown below

Asserting when saga has only one PUT effect object

Read More about testing sagas using redux-saga-test-plan here

If you liked reading this, don’t forget to click 👏 and add claps for the post. You can also follow me on on twitter @karanjariwala47 for java-script or react updates.