Skip to content

Instantly share code, notes, and snippets.

@andrewmclagan
Last active May 7, 2020 11:51
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andrewmclagan/e25983ebda4e49ed28103839ee00b839 to your computer and use it in GitHub Desktop.
Save andrewmclagan/e25983ebda4e49ed28103839ee00b839 to your computer and use it in GitHub Desktop.
[6.x.x] ReduxForm testing

Test harness setup

You may choose a testing stack as you please, with a few exceptions they are all very similar in their functioanlity and APIs. For the purposees of this document we will be using Ava, Enzyme, JSDom and Sinon.

  • Ava a lightning quick test runner and assertion library.

  • Enzyme a react component test utility allowing us to access the DOM with jQuery like syntax.

  • JSDom headless javascript browser for DOM.

  • Sinon for mocking out any function behaviour.

Seting up these libraries is out of scope with this document, please refer to the relevant docs and the testing redux-form example repository.

What to test?

When testing React components it's important to understand what actually needs test coverage. We only want to test the behaviour of our <CreateUser /> form component, not the underlying implimentations involved in that behaviour. What does that mean? Good question!

For example in our component we pass in a property called createUser, a function that sends the data back to our server. This is called within the submit() function of the component that is inturn called via handleSubmit() property passed in by redux-form.

Both the createUser and handleSubmit functions can be considered external dependancies. In the case of handleSubmit it has test coverage within the redux-form library itself. Also you should have your own tests giving coverage to the createUser API functionality. Therefore we DO NOT write test for these. Another example of this concept: If we were using a react-boostrap button as our submit button, we DO NOT want to test how this button works, its an external dependancy with its own test.

Let's break down the behaviour of our form:

  • It renders a single <form /> element
  • It renders the correct form <input /> elements
  • It renders a submit <button />
  • The form is submitted when the button is clicked
  • When the form is submitted handle submit is called with our components submit function.

How to test?

Test in isolation

Firstly as we are only testing the behaviour of our <CreateUser /> component and not the redux-form library, we want to export a non-connected / decorated version of our component to test. You will notice at the bottom of the component we export the redux-form conntected version as the default export. This makes it easy to use within our application. Also created a named export, that exports the isolated - component only - version. If this is confusing please readup on ES6 module exports / imports before proceeding.

import React, { Component, PropTypes } from 'react';
import { reduxForm, Field } from 'redux-form';
export class CreateUser extends Component {
static propTypes = {
createUser: PropTypes.func.isRequired,
initialValues: PropTypes.object,
submitting: PropTypes.bool.isRequired,
invalid: PropTypes.bool.isRequired,
handleSubmit: PropTypes.func.isRequired,
}
submit = (values) => {
return this.props.createUser(values.first_name, values.last_name, values.bio, values.email, values.username)
.catch(response => { throw response.errors; });
}
render() {
const { invalid, submitting } = this.props;
return (
<form className="create-user-form" onSubmit={this.props.handleSubmit(this.submit)}>
<div className="row">
<div className="col-md-6">
<Field name="first_name" component="input" type="text" placeholder="First name" />
</div>
<div className="col-md-6">
<FormField name="last_name" component="input" type="text" placeholder="Last name" />
</div>
</div>
<div className="row">
<div className="col-md-6">
<FormField name="email" component="input" type="text" placeholder="Email address" />
</div>
<div className="col-md-6">
<FormField name="username" component="input" type="text" placeholder="Username" />
</div>
</div>
<label htmlFor="">Something about you</label>
<FormField name="bio" component="textarea" />
<button type="submit" className="create" disabled={submitting || invalid}>Sign Up</button>
</form>
);
}
}
export default reduxForm({
form: 'create-user',
})(CreateUser);
import test from 'ava';
import React from 'react';
import sinon from 'sinon';
import { shallow, mount, render } from 'enzyme';
import { CreateUser } from './CreateUser';
const defaultProps = {
editProfile: () => {},
submitting: false,
handleSubmit: () => {},
invalid: false,
initialValues: {},
};
test('it renders a <form /> element', t => {
const wrapper = shallow(<ProfileForm {...defaultProps} />);
t.true(wrapper.find('form').is('.create-user-form'));
});
test('it renders one <Button /> component', t => {
const wrapper = shallow(<ProfileForm {...defaultProps} />);
t.is(wrapper.find('button[type="submit"]').length, 1);
});
test('it renders the correct form elements', t => {
const wrapper = shallow(<ProfileForm {...defaultProps} />);
t.is(wrapper.find('[name="first_name"]').length, 1);
t.is(wrapper.find('[name="last_name"]').length, 1);
t.is(wrapper.find('[name="email"]').length, 1);
t.is(wrapper.find('[name="username"]').length, 1);
t.is(wrapper.find('[name="bio"]').length, 1);
});
test('calls handleSubmit on form submission', t => {
const handleSubmit = sinon.spy();
const wrapper = shallow(<ProfileForm {...defaultProps} handleSubmit={handleSubmit} />);
wrapper.find('Button').simulate('click');
t.true(handleSubmit.calledWith(wrapper.instance().submit));
});
@altaywtf
Copy link

hello, you are rendering undefined <ProfileForm /> component instead of <CreateUser /> you've imported on line 6.

@kulpreet
Copy link

kulpreet commented Nov 3, 2016

Also, shallow won't test much, you might want to do a mount, and then you will notice the error redux-form/redux-form#849

@gusverdywid
Copy link

The last test doesn't seem to work, please see: enzymejs/enzyme#308. Enzyme doesn't support event propagation which is needed for the button to trigger onSubmit on the form. Any other solution to make it work on the last test?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment