Skip to content

Instantly share code, notes, and snippets.

@molily
Last active December 2, 2015 09:19
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save molily/4071110c15f8d3b53071 to your computer and use it in GitHub Desktop.
Save molily/4071110c15f8d3b53071 to your computer and use it in GitHub Desktop.
Helpers for testing React / Redux smart components (Jasmine expectations)

In smart components, we’re exporting the original React component class and the Connect wrapper class.

  • There is a test for the smart component class that checks whether mapStateToProps and mapDispatchToProps generate and pass through the expected props.
  • There is another, more conventional test for the dumb component class that tests the rendered DOM tree.

The tests helpers here are used for the smart test.

// Creates a stub for a Redux store with the methods getState, subscribe
// and dispatch. subscribe and dispatch are just spies without behavior.
// https://github.com/rackt/redux/blob/master/docs/basics/Store.md
// https://github.com/rackt/redux/blob/master/docs/Glossary.md#store
export default (state) => {
const getState = () => state;
const subscribe = jasmine.createSpy('createStoreStub.subscribe');
const dispatch = jasmine.createSpy('createStoreStub.dispatch');
return { getState, subscribe, dispatch };
};
import _ from 'lodash';
import React from 'react';
import TestUtils from 'react-addons-test-utils';
import createStoreStub from '__tests__/createStoreStub';
// Tests a smart component that is bound to stores and actions directly.
// Tests if the props are passed correctly to the dumb component.
// Expects
// - a smart component,
// - an object with state properties that are expected on the props,
// - an array of action creator names.
// Returns the props of the rendered dumb component.
//
// Usage:
//
// Implementation:
// class DumbComponent extends React.Component {
// render() {
// return <button onClick={this.props.login}>
// {this.props.i18n.loginButtonLabel}
// </button>;
// }
// }
// DumbComponent.propTypes = {
// i18n: PropTypes.object.isRequired,
// login: PropTypes.func.isRequired,
// };
// const SmartComponent = connect(
// // mapStateToProps
// (state) => ({ i18n: state.i18n }),
// // mapDispatchToProps
// { login: userActions.login }
// )(DumbComponent);
//
// Test:
// const state = { i18n: { loginButtonLabel: 'Login' } };
// const actionCreatorNames = [ 'login' ];
// testSmartComponent(SmartComponent, state, actionCreatorNames);
export default (SmartComponent, state, actionCreatorNames) => {
// Create a store, pass it to the SmartComponent as context
const store = createStoreStub(state);
const context = { store };
const shallowRenderer = TestUtils.createRenderer();
shallowRenderer.render(<SmartComponent/>, context);
const tree = shallowRenderer.getRenderOutput();
const { props } = tree;
// mapStateToProps test
_.forEach(state, (value, key) => {
expect(props[key]).toBe(value);
});
// mapDispatchToProps test
_.forEach(actionCreatorNames, (actionCreatorName) => {
// Call the bound action creator. This is the only way to check that the
// the wrapped value is a function. This calls the original action creator,
// but since we’re using redux-thunk, it only returns a function.
props[actionCreatorName]();
});
return props;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment