# redux-saga-tester

> Full redux environment testing helper for redux-saga

Latest version **1.0.874** (published 2020-09-02) · MIT license · 0 weekly downloads

## Install

```sh
npm install redux-saga-tester
pnpm add redux-saga-tester
yarn add redux-saga-tester
bun add redux-saga-tester
```

## Health

**Score 23/100 (F)** — status: abandoned.

Positive: has types package; no vulnerabilities; high quality score.

Warnings: low downloads; no esm support.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 1.0.874 |
| Published | 2020-09-02 |
| First published | 2016-09-07 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | separate (@types/redux-saga-tester) |
| Module format | CommonJS |
| Dependencies | 1 |
| Unpacked size | 52.6 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 244 |
| Author | Guy Benron |
| Maintainers | cijoe, guybe, netanelgilad, shahata, tyv, wix-ambassador, wix-bi-publisher, wix-ci, wix-ci-publisher |
| Keywords | redux-saga, test |

## Links

- npm: https://www.npmjs.com/package/redux-saga-tester
- Repository: https://github.com/wix/redux-saga-tester
- Homepage: https://github.com/wix/redux-saga-tester#readme
- Issues: https://github.com/wix/redux-saga-tester/issues
- npm.io page: https://npm.io/package/redux-saga-tester

## Dependencies (1)

- [babel-runtime](https://npm.io/package/babel-runtime.md) ^6.11.6

## Alternatives

- [duck](https://npm.io/package/duck.md) — 4.2M weekly downloads
- [ava](https://npm.io/package/ava.md) — 560.2K weekly downloads
- [storybook-addon-module-mock](https://npm.io/package/storybook-addon-module-mock.md) — 71.7K weekly downloads
- [vest](https://npm.io/package/vest.md) — 50.1K weekly downloads
- [@ethereum-waffle/mock-contract](https://npm.io/package/@ethereum-waffle/mock-contract.md) — 40.0K weekly downloads

## Recent versions

- 1.0.874 (latest) — 2020-09-02
- 1.0.873 — 2020-09-01
- 1.0.872 — 2020-08-31
- 1.0.871 — 2020-08-30
- 1.0.870 — 2020-08-29
- 1.0.869 — 2020-08-28
- 1.0.868 — 2020-08-27
- 1.0.867 — 2020-08-26
- 1.0.866 — 2020-08-25
- 1.0.865 — 2020-08-24
- 1.0.864 — 2020-08-13
- 1.0.863 — 2020-08-12
- 1.0.862 — 2020-08-11
- 1.0.861 — 2020-08-10
- 1.0.860 — 2020-08-09
- … 860 more at https://npm.io/package/redux-saga-tester/versions

## README

# redux-saga-tester

Full redux environment testing helper for redux-saga.

[redux-saga](https://github.com/yelouafi/redux-saga/) is a great library that provides an easy way to test your sagas step-by-step, but it's tightly coupled to the saga implementation. Try a non-breaking reorder of the internal `yield`s, and the tests will fail.

This tester library provides a full redux environment to run your sagas in, taking a black-box approach to testing. You can dispatch actions, observe the state of the store at any time, retrieve a history of actions and listen for specific actions to occur.

# Getting Started

## Installation

```
$ npm install --save-dev redux-saga-tester
```

## Basic Example

Suppose we have a saga that waits for a START action, performs some async (or sync) actions (eg. fetching data from an API), and dispatches a `SUCCESS` action upon completion. Here's how we would test it:

```js
import ourSaga from './saga';

describe('ourSaga test', () => {
    let sagaTester = null;

    beforeEach(() => {
        // Init code
        sagaTester = new SagaTester({ initialState });
        sagaTester.start(ourSaga);
    });

    it('should retrieve data from the server and send a SUCCESS action', async () => {
        // Our test (Actions is our standard redux action component). Start the saga with the START action
        sagaTester.dispatch(Actions.actions.start());

        // Wait for the saga to finish (it emits the SUCCESS action when its done)
        const successAction = await sagaTester.waitFor(Actions.types.SUCCESS);

        // Check that the success action is what we expect it to be
        expect(successAction).to.deep.equal(
            Actions.actions.success({ data: expectedData })
        );
    });
});
```

This is of course an example of testing a saga that contains async actions. Generally when testing it is perferred to use sync mocks. In that case, there's no need to async/await.

## Full example

Can be found under the `examples` directory.

```js
import chaiAsPromised from 'chai-as-promised';
import { call, take, put } from 'redux-saga/effects';
import SagaTester from 'redux-saga-tester';

chai.use(chaiAsPromised);

const someValue = 'SOME_VALUE';
const someResult = 'SOME_RESULT';
const someOtherValue = 'SOME_OTHER_VALUE';
const middlewareMeta = 'MIDDLEWARE_TEST';
const fetchRequestActionType = 'FETCH_REQUEST'
const fetchSuccessActionType = 'FETCH_SUCCESS'

const initialState = { someKey : someValue };
const reducer = (state = someValue, action) =>
    action.type === fetchSuccessActionType ? someOtherValue : state;
const middleware = store => next => action => next({
    ...action,
    meta : middlewareMeta
});
// options are passed to createSagaMiddleware
const options = { onError => console.error.bind(console) }
const fetchApi = () => someResult;

const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms))

function* listenAndFetch() {
    yield take(fetchRequestActionType);
    const result = yield call(fetchApi);
    yield call(delay, 500); // For async example.
    yield put({ type : fetchSuccessActionType, payload : result });
}

it('Showcases the tester API', async () => {
    // Start up the saga tester
    const sagaTester = new SagaTester({
        initialState,
        reducers : { someKey : reducer },
        middlewares : [middleware],
        options,
    });
    sagaTester.start(listenAndFetch);

    // Check that state was populated with initialState
    expect(sagaTester.getState()).to.deep.equal(initialState);

    // Dispatch the event to start the saga
    sagaTester.dispatch({type : fetchRequestActionType});

    // Hook into the success action
    await sagaTester.waitFor(fetchSuccessActionType);

    // Check that all actions have the meta property from the middleware
    sagaTester.getCalledActions().forEach(action => {
        expect(action.meta).to.equal(middlewareMeta)
    });

    // Check that the new state was affected by the reducer
    expect(sagaTester.getState()).to.deep.equal({
        someKey : someOtherValue
    });

    // Check that the saga listens only once
    sagaTester.dispatch({ type : fetchRequestActionType });
    expect(sagaTester.numCalled(fetchRequestActionType)).to.equal(2);
    expect(sagaTester.numCalled(fetchSuccessActionType)).to.equal(1);

    // Reset the state and action list, dispatch again
    // and check that it was called
    sagaTester.reset(true);
    expect(sagaTester.wasCalled(fetchRequestActionType)).to.equal(false);
    sagaTester.dispatch({ type : fetchRequestActionType });
    expect(sagaTester.wasCalled(fetchRequestActionType)).to.equal(true);
})
```

## API

#### `new SagaTester(options) => sagaTester`

Create a new SagaTester instance.

1. `options: Object`
   * `initialState : Object`
   * `reducers : Object | Function`
   * `middlewares : Array[Function]`
   * `combineReducers : Function`
   * `ignoreReduxActions : Boolean`
   * `options : Object`
     * Options for `createSagaMiddleware` (see [docs](https://github.com/redux-saga/redux-saga/tree/master/docs/api#createsagamiddlewareoptions)).

#### `sagaTester.start(saga, [...args])`
Starts execution of the provided saga.

1. `saga : Function`
    * The saga generator function to start
2. `[...args] : Any`
    * *Optional* Arguments to pass to the generator on start

#### `sagaTester.dispatch(action)`

Dispatches an action to the redux store.

#### `sagaTester.updateState(newState)`

Assigns the `newState` into the current state.
_(Only works with the default reducer.)_

#### `sagaTester.getState() => Object`

Returns the state of the redux store.

#### `sagaTester.waitFor(actionType, futureOnly) => Promise<action>`

Returns a promise that will resolve if the specified action is dispatched to the store.

1. `actionType : String`
2. `futureOnly : Boolean`
   * Causes waitFor to only resolve if the action is called in the future.

The promise resolves with the matching `action` object.

#### `sagaTester.wasCalled(actionType) => Boolean`

Returns whether the specified was dispatched in the past.

#### `sagaTester.numCalled(actionType) => Number`

Returns the number of times an action with the given type was dispatched.

#### `sagaTester.getLatestCalledAction() => Action`

Returns the last action dispatched to the store.

#### `sagaTester.getCalledActions() => Array[Actions]`

Returns an array of all actions dispatched.

#### `sagaTester.reset(clearActionList)`

Reset the store state back to `initialState`.

1. `clearActionList : Boolean`
   * Clears the history of past actions (defaults to `false`).

---
_Source: https://npm.io/package/redux-saga-tester · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
