redux-saga-tester

repository·master·Indexed 42 days ago

https://github.com/wix-incubator/redux-saga-tester

A testing helper for redux-saga version 1.0.1 that provides a full Redux environment for black-box testing. It allows developers to dispatch actions, observe store state, and use waitFor() to resolve promises when specific actions are dispatched, reducing test sensitivity to internal implementation changes.

Tokens
1.6K
Snippets
9
Records
10
Agent score
4%

What's inside redux-saga-tester

  1. How redux-saga-tester works

    master

    Unlike standard redux-saga testing methods that rely on step-by-step yield inspection (which is fragile and tightly coupled to implementation details), redux-saga-tester uses a black-box approach.

    It provides a full Redux environment to run your sagas in. This allows you to:

    • Dispatch actions to trigger sagas.
    • Observe the state of the store at any time.
    • Retrieve a history of all dispatched actions.
    • Listen for specific actions to occur using promises.
  2. Initialize SagaTester with options

    master

    Create a new SagaTester instance by passing an options object. This allows you to configure the Redux environment including state, reducers, and middleware.

    Options Object Properties:

    • initialState: Object - The starting state of the store.
    • reducers: Object | Function - Reducers to manage state.
    • middlewares: Array[Function] - An array of Redux middlewares.
    • combineReducers: Function - A function to combine reducers.
    • ignoreReduxActions: Boolean - Whether to ignore certain Redux actions.
    • options: Object - Configuration options passed directly to createSagaMiddleware (e.g., onError).
    const sagaTester = new SagaTester({
        initialState,
        reducers: { someKey: reducer },
        middlewares: [middleware],
        options: { onError: console.error.bind(console) },
    });
  3. Initialize SagaIntegrationTester

    master

    To use the tester, instantiate SagaIntegrationTester with a configuration object. This sets up a Redux store integrated with redux-saga and a custom middleware that tracks dispatched actions.

    Configuration Options:

    • initialState (Object): The starting state for the Redux store.
    • reducers (Object|Function): A single reducer function or an object of reducers (which will be combined using combineReducers).
    • middlewares (Array): Additional middlewares to include in the store.
    • combineReducers (Function): The function used to combine reducers (defaults to redux.combineReducers).
    • ignoreReduxActions (Boolean): If true, actions starting with @@redux/ or the UPDATE_STATE_TYPE will not be tracked in calledActions.
    • options (Object): Options passed directly to createSagaMiddleware.
    import SagaIntegrationTester from 'redux-saga-tester';
    
    const tester = new SagaIntegrationTester({
        initialState: { user: null },
        reducers: { user: userReducer },
        ignoreReduxActions: true
    });
  4. Update Redux state with updateState()

    master

    You can manually update the state of the internal Redux store using updateState(newState). This is useful for setting up specific test scenarios (e.g., simulating a logged-in user) without dispatching complex action sequences.

    Note: setState() is deprecated in favor of updateState().

    tester.updateState({ user: { id: 1, name: 'Test User' } });
  5. Reset the tester state

    master

    The reset(clearActionList = false) method resets the Redux store to its initialState.

    • If clearActionList is true, it also clears the calledActions history and the internal action lookup map. This is useful when running multiple sequential tests using the same tester instance.
    // Reset store to initialState and clear action history
    tester.reset(true);
    
    // Reset store to initialState but keep action history
    tester.reset();
  6. Wait for specific actions with waitFor()

    master

    Use waitFor(actionType, futureOnly = false) to return a Promise that resolves when a specific action type is dispatched. This is useful for testing asynchronous flows in sagas.

    • actionType (String): The type of the action to wait for.
    • futureOnly (Boolean): If true, the promise will only resolve for actions dispatched after waitFor is called, even if the action has already been dispatched.

    If the saga finishes without the action being called, the promise will reject with an error: '{actionType} was waited for but never called'.

    // Wait for an action to happen
    await tester.waitFor('FETCH_USER_SUCCESS');
  7. Inspect dispatched actions

    master

    The tester provides several methods to inspect the history of actions dispatched during the test:

    • getCalledActions(): Returns a shallow copy of all actions dispatched through the store.
    • getLatestCalledAction(): Returns the most recent action dispatched.
    • getLatestCalledActions(num = 1): Returns the last num actions dispatched.
    • wasCalled(actionType): Returns true if the specified action type has been dispatched at least once.
    • numCalled(actionType): Returns the total count of times a specific action type was dispatched.
    const actions = tester.getCalledActions();
    const lastAction = tester.getLatestCalledAction();
    
    if (tester.wasCalled('DATA_LOADED')) {
        console.log('Action was seen');
    }
  8. Start sagas with start()

    master

    The start(sagas, ...args) method runs the provided sagas using the internal sagaMiddleware. It returns the task object produced by redux-saga.

    It also attaches listeners to the task to:

    1. Verify that any actions requested via waitFor() were actually called (rejects if not).
    2. Catch and propagate root saga exceptions by rejecting any pending waitFor() promises.
    // sagas is an array of saga functions
    tester.start([myRootSaga], arg1, arg2);