1.0.0 • Published 6 years ago

redux-reactive v1.0.0

Weekly downloads
4
License
MIT
Repository
github
Last release
6 years ago

Redux Reactive

Redux Reactive is a testing helper library for Redux powered applications.

In a Nutshell

Plug it into your Redux store (as a middleware) and let it listen to all dispatched actions, while also keeping a list and some valuable data along the way.

Once plugged in to your store, ask Redux Reactive to waitFor(x) an action of x type to be dispatched and it will return a promise which will resolve whenever the action is dispatched. Providing you with that valuable data we mentioned earlier.

Quick React example

  1. Install Using yarn: yarn add -D redux-reactive

Using npm npm install --saveDev redux-reactive

  1. Plug it to your store store.js
import { createStore, applyMiddleware, compose } from 'redux';
import { reactiveMiddleware } from 'redux-reactive'; // import the reactive middleware
import thunk from 'redux-thunk';

import rootReducer from './rootReducer';

let enhancer = compose(applyMiddleware(middleware, thunk));
cont initialState = {};

const store = createStore(
  rootReducer,
  initialState,
  enhancer
);

export default store;
  1. Use it in your tests
import React from 'react';
import { mount } from 'enzyme';
import { Provider } from 'react-redux';

import ReduxReactive from 'redux-reactive'; // <- import Redux Reactive

import store from './store';                // <- your store
import TodoForm from './index';             // <- your container (a redux-form)

import {
  CREATE_TODO,
} from './todoActions';                     // <- your expected action type

const reactive = new ReduxReactive();       // get the instance

describe('<TodoForm /> - Redux', () => {
  let options;

  const wrapInProvider = props => {
    options = {
      context: { store },                   // use store as context
      childContextTypes: {
        store: PropTypes.object.isRequired,
      },
    };

    return mount(
      <Provider store={store}>              // pass the store to Provider
        <TodoForm {...props} />
      </Provider>,
      options,
    );
  };

  beforeEach(() => {
    reactive.clear();                       // Clear any listeners/history
  });

  test('It successfully creates a todo', done => {

    // Ask Redux Reactive to wait for an action with type CREATE_TODO to be dispatched
    reactive.waitFor(CREATE_TODO).then((action, history) => {

      /* once the action is dispatched, the promise will resolve
        and provide you with two parameters: action and history.

        The action parameter contains the actual action you've been waiting for.
        The history parameter contains the list of actions dispatched since the last clear() call.
      */

      // you can read the state as it was right after the action was processed by your reducers.
      const state = action.stateAfter();

      // and make assertions based on the state
      expect(state.items).toHaveLength(1);


      // you can get the state mutation at that point in time
      const mutation = action.stateMutation();

      // validate that expected mutations are there
      expect(mutation).toEqual({ items: { 0: { title: 'use redux reactive', completed : false } } });

      done(); // let mocha know we are done
    });

    // no todos exist prior data fetches
    const state = store.getState();
    expect(state.items).toHaveLength(0);

    const wrapper = wrapInProvider({});

    // simulate interaction with redux-form (fill and submit)
    const todoForm = wrapper.find('form');
    const todoFormInput = wrapper.find('.todo-form-input').first();
    todoFormInput.simulate('change', { target: { value: 'use redux reactive' } });
    todoForm.simulate('submit');
  });
});

The Motivation, a Goal

If you want to know how it all started and what goal we are trying to achieve, please check the Motivations section.

Redux-oriented + Framework-agnostic

Redux Reactive was designed to be Redux-oriented and Framework-agnostic so you can use it to test/debug any application that uses Redux as the state container.

Features

Custom assertion API

It wraps the collected actions in a convenient wrapper class that helps you filter out irrelevant actions or return all actions dispatched after certain action.

Every action is also wrapped in another class that provides an extra level of API focused in the action's properties and side effects.

Time Travel

Just like the fantastic redux-devtools-extension, it allows you to travel back and forward in time by tracking each action' side effects. You can access this feature via each wrapped action stateBefore() and stateAfter() methods.

Reactive (event driven)

When you subscribe to an action, you get a promise that is fulfilled whenever such action is dispatched. Then you perform the assertions you want. It means that your assertions are ran in reaction to the action being dispatched.

Seen in another way, your actions become events and you react to those events by running your assertions.

Not Intrusive

It doesn't matter what test runner / assertion library you use, Redux Reactive's assertion API returns literals that can be easily understood/asserted by the modern libraries.

Examples

See the Examples section.