There are two main ways to test Sagas: testing the saga generator function step-by-step or running the full saga and asserting the side effects.
Suppose we have the following actions:
We want to test the saga:
Since Sagas always yield an Effect, and these effects have basic factory functions (e.g. put, take etc.) a test may
inspect the yielded effect and compare it to an expected effect. To get the first yielded value from a saga,
A value must then be returned to assign to the
action constant, which is used for the argument to the
Since there are no more
yields, then next time
next() is called, the generator will be done:
Sometimes your saga will have different outcomes. To test the different branches without repeating all the steps that lead to it you can use the utility function cloneableGenerator
This time we add two new actions,
DO_STUFF, with a related action creators:
Now the saga under test will put two
DO_STUFF actions before waiting for a
CHOOSE_NUMBER action and then putting
changeUI('blue'), depending on whether the number is even or odd.
The test is as follows:
See also: Task cancellation for testing fork effects
Although it may be useful to test each step of a saga, in practise this makes for brittle tests. Instead, it may be preferable to run the whole saga and assert that the expected effects have occurred.
Suppose we have a basic saga which calls an HTTP API:
We can run the saga with mocked values:
A test could then be written to assert the dispatched actions and mock calls:
See also: Repository Examples:
While both of the above testing methods can be written natively, there exist several libraries to make both methods easier. Additionally, some libraries can be used to test sagas in a third way: recording specific side-effects (but not all).
Sam Hogarth's (@sh1989) article summarizes the different options well.
For testing each generator yield step-by-step there is
redux-saga-test-engine is for recording and testing for specific side effects. For an integration test,
redux-saga-test-plan can actually cover all three bases.
redux-saga-testing for step-by-step testing#
redux-saga-test library provides syntactic sugar for your step-by-step tests. The
fromGenerator function returns a value that can be iterated manually with
.next() and have an assertion made using the relevant saga effect method.
redux-saga-testing library provides a method
sagaHelper that takes your generator and returns a value that works a lot like Jest's
it() function, but also advances the generator being tested. The
result parameter passed into the callback is the value yielded by the generater
This is the most versatile library. The
testSaga API is used for exact order testing and
expectSaga is for both recording side-effects and integration testing.
This library functions very similarly in setup to
redux-saga-test-plan, but is best used to record effects. Provide a collection of saga generic effects to be watched by
createSagaTestEngine function which in turn returns a function. Then provide your saga and specific effects and their arguments.
The value of
actualEffects is an array containing elements equal to the yielded values from all collected effects, in order of occurence.
A final library to consider for integration testing. this library provides a
sagaTester class, to which you instantiate with your store's initial state and your reducer.
To test your saga, the
start() method with your saga and its argument(s). This runs your saga to its end. Then you may assert that effects occured, actions were dispatched and the state was updated as expected.
Provides a native way to perform integration like testing without one of the above libraries.
The idea is that you can create a real redux store with saga middleware in your test file. The saga middleware takes an object as an argument. That object would have an
effectMiddlewares value: a function where you can intercept/hijack any effect and resolve it on your own - passing it very redux-style to the next middleware.
In your test, you would start a saga, intercept/resolve async effects with effectMiddlewares and assert on things like state updates to test integration between your saga and a store.
Here's an example from the docs: