Skip to content

smaato/react-test-kit

Repository files navigation

React Test Kit

These tools make it easier for us to test apps built with React and Redux.

Dependencies

  • Node 4.x.x
  • Browserify with the Babelify transform
  • Jasmine (for some tools, see below)
  • React, react-dom, react-addons-test-utils
  • Redux, redux-thunk, react-redux
  • Sizzle

Getting Started

Setup & Run

npm install
npm install -g gulp
gulp

Usage

Just import the tools you want into your test file:

import {
  ContainerTestCase,
  MockedRouteActions,
  MockedStore,
  mockFormStore,
  TestCase,
  TestCaseFactory,
} from 'react-test-kit';

Tools

TestCaseFactory

Use this factory to test React components' state by passing in various props, inspecting their DOM, and simulating user interactions.

WARNING: Methods for finding child Components won't work unless the root component is a composite Component (e.g. a Component you've defined), not a native Component (e.g. div or table). This seems related to this React issue.

// Construction via factory methods:
it('accepts a ReactElement and returns a TestCase instance', () => {
  const props = {
    property1: 'value1',
  };
  const testCase = TestCaseFactory.createFromElement(<TestElement {...props} />);
  expect(testCase instanceof TestCase).toBe(true);
});

it('accepts a ReactClass and returns a TestCase instance', () => {
  const props = {
    property1: 'value1',
    children: <div>A child</div>,
  };
  // Alternatively, you can use TestCaseFactory.createFromClass here.
  const testCase = TestCaseFactory.create(TestElement, props);
  expect(testCase instanceof TestCase).toBe(true);
});

it('accepts a function and returns a TestCase instance', () => {
  const props = {
    property1: 'value1',
  };
  // Alternatively, you can use TestCaseFactory.createFromFunction here.
  const testCase = TestCaseFactory.create(StatelessTestElement, props);
  expect(testCase instanceof TestCase).toBe(true);
});

// The testCase instance surfaces a method for triggering events and simulating
// user interaction. It supports all of the same events that React supports.
testCase.trigger('click', optDomNode, optEventData);

// You can query its DOM.
const nodes = testCase.find('div');
const node = testCase.first('div');

// You can also query it for React components.
const elements = testCase.findComponents(ChildComponent);
const element = testCase.firstComponent(ChildComponent);

ContainerTestCase

Use this class to test that Redux container classes are providing the right properties and actions.

Note: This class dynamically creates its own Jasmine assertions (so it depends upon Jasmine).

// Given a Container that looks like this:
function mapStateToProps(state) {
  return {
    prop1: state.reducerSource.something,
    prop2: undefined,
  };
}

function mapDispatchToProps(dispatch) {
  const actions = {
    action1: () => {},
    action2: () => {},
  };
  return bindActionCreators(actions, dispatch);
}

const TestContainer = connect(mapStateToProps, mapDispatchToProps)(() => {
});

// You have to pass it into `ContainerTestCase` with a mock state object,
// and then run tests on it, like this:
const testCase = new ContainerTestCase(TestContainer, {
  reducerSource: {},
});

describe('Props', () => {
  testCase.expectProps([
    'prop1',
    'prop2',
  ]);
});

describe('Action creators', () => {
  testCase.expectActionCreators([
    'action1',
    'action2',
  ]);
});

MockedStore

Use this class to test that asynchronous Redux action creators create the right actions in the right order.

it('asserts that asynchronous actions are created in sequence', (done) => {
  const actionCreator = () => (dispatch) => {
    dispatch({
      type: 'A',
    });
    setTimeout(() => {
      dispatch({
        type: 'B',
      });
    }, 10);
  };
  const expectedActions = [{
    type: 'A',
  }, {
    type: 'B',
  }];
  const store = new MockedStore();
  store.expectActionsFromCreator(actionCreator(), expectedActions, done);
});

it('references the mocked state object', (done) => {
  const state = {
    prop: 'value',
  };
  const actionCreator = () => (dispatch, getState) => {
    dispatch({
      payload: getState().prop,
    });
  };
  const expectedActions = [{
    payload: state.prop,
  }];
  const store = new MockedStore(state);
  store.expectActionsFromCreator(actionCreator(), expectedActions, done);
});

mockFormStore

Call this function to get a store instance that uses the redux-form reducer.

Given a component that uses redux-form and looks something like this:

import React, {
  Component,
} from 'react';
import {reduxForm} from 'redux-form';

class AccountForm extends Component {
  // ...
}

AccountForm = reduxForm({
  form: 'accountForm',
  fields: [ /* ... */ ],
})(AccountForm);

export default AccountForm;

You can create a spec that tests it like this:

import React from 'react';
import { Provider } from 'react-redux';
import {
  TestCaseFactory,
  mockFormStore,
} from 'react-test-kit';

import AccountForm from './AccountForm.jsx';

describe('AccountForm', () => {
  it('is testable', () => {
    const store = mockFormStore();

    const props = {
      // ...
    };

    const testCase = TestCaseFactory.createFromElement(
      <Provider store={store}>
        <AccountForm {...props} />
      </Provider>
    );

    // expect() something
  });
});

MockedRouteActions

Use this class to create redux-router actions for testing action creators that affect the route (e.g. redirects).

Note: We've since migrated to react-router-redux so this tool is no longer being supported and will be removed in the near future.

const state = {};
const pathname = '/testPath';
const action = MockedRouteActions.replaceState(state, pathname);
expect(action).toEqual({
  type: '@@reduxReactRouter/historyAPI',
  payload: {
    method: 'replaceState',
    args: [
      state,
      pathname,
    ],
  },
});

About

Tools for testing React components, and Redux actions and containers.

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors