redux-reactive v1.0.0
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
- Install
Using yarn:
yarn add -D redux-reactive
Using npm
npm install --saveDev redux-reactive
- 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;
- 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.
6 years ago