patternMinor
Unit tests for React component to submit an input form with validation
Viewed 0 times
withreacttestsvalidationinputsubmitformforcomponentunit
Problem
I'm very new to front-end/unit testing and have been having a difficult time understanding the point altogether, but I managed to push my way through testing literally everything I could possibly think of.
Anyways, if anyone has the time I'd love a review of my component and tests for said component.
Component
Tests
```
// dependencies
import React from 'react';
import { shallow, mount } from 'enzyme';
import { spy } from 'sinon';
// components
import InputForm from './InputForm';
describe('', () => {
let props, wr
Anyways, if anyone has the time I'd love a review of my component and tests for said component.
Component
import React, { Component } from 'react';
import './InputForm.css';
class InputForm extends Component {
constructor(props) {
super(props);
this.onFormSubmit = this.onFormSubmit.bind(this);
this.validate = this.validate.bind(this);
this.onInputChange = this.onInputChange.bind(this);
this.state = {
fields: {},
fieldErrors: {},
};
}
onInputChange(e) {
const fields = this.state.fields;
const newFields = {};
newFields[e.target.name] = e.target.value;
this.setState({
fields: {...fields, ...newFields}
});
}
validate(formData) {
const errors = {};
if (!formData.name || formData.name === '' || formData.name === null) {
errors.name = 'Please enter your name.';
}
return errors;
}
onFormSubmit(e) {
e.preventDefault();
const formData = this.state.fields
const fieldErrors = this.validate(formData);
this.setState({
fieldErrors
});
if (Object.keys(fieldErrors).length) return;
const name = this.state.fields.name;
this.props.handleFormSubmit(name);
this.setState({
fields: {},
fieldErrors: {},
})
}
render() {
return (
this.onFormSubmit(e)}>
this.onInputChange(e)}
/>
{this.state.fieldErrors.name}
);
}
}
InputForm.propTypes = {
handleFormSubmit: React.PropTypes.func.isRequired,
};
export default InputForm;Tests
```
// dependencies
import React from 'react';
import { shallow, mount } from 'enzyme';
import { spy } from 'sinon';
// components
import InputForm from './InputForm';
describe('', () => {
let props, wr
Solution
One thing that could help a lot is to realize that tests verify behavior, not implementation.
This means you shouldn't be validating these things:
Why you say? Well, why do we write tests? Many reasons, but one big one is because it gives us the ability to alter implementation without modifying behavior (refactoring). Said another way, implementations change, and often behavior should not.
Tests should only verify the external interface of the things-under-test, and the things-under-test can fall on a gradient of complexity starting from the simplest, a single function, to an object, on to an entire service or set of objects, and on to entire applications (e.g. a web app).
In this case, you're testing a component, so the external interface includes anything passed to the component (props, context, etc.), ways of interacting with the component (buttons and other input handlers), and the return value of the component (rendered element tree). Your test titled "should display an error when no value is input" is a great example of this. A pattern I follow with any tests is: given (setup), when (stimulation – aka .simulate()), then (assertion), and your test does this well.
Ultimately, you won't understand the point of testing until:
... and then you'll be scared to do anything without it. In my 15 years of writing software, this fear is one of the most common reasons software projects fail (after market pressures).
This means you shouldn't be validating these things:
- should have a `
element
element should have a onSubmit attribute
- onSubmit attribute should be of type function
element should have anelement
element should be of typetext
element should have an
element
element should have a className
element should have an element
element should be of type submit
element should have a className
element should have a value attribute
element should have an onChange attribute
- onChange attribute should be of type
function
These things are good, but the descriptions should be rewritten with a focus on the behavior, not the implementation:
element should have a placeholder attribute with value Name
element value should be empty (2x of these)
- should update the state when a value is input
- should display an error when no value is input
element should be null when passed validationError: false
element should be Please enter your name` when passed validationError: trueWhy you say? Well, why do we write tests? Many reasons, but one big one is because it gives us the ability to alter implementation without modifying behavior (refactoring). Said another way, implementations change, and often behavior should not.
Tests should only verify the external interface of the things-under-test, and the things-under-test can fall on a gradient of complexity starting from the simplest, a single function, to an object, on to an entire service or set of objects, and on to entire applications (e.g. a web app).
In this case, you're testing a component, so the external interface includes anything passed to the component (props, context, etc.), ways of interacting with the component (buttons and other input handlers), and the return value of the component (rendered element tree). Your test titled "should display an error when no value is input" is a great example of this. A pattern I follow with any tests is: given (setup), when (stimulation – aka .simulate()), then (assertion), and your test does this well.
Ultimately, you won't understand the point of testing until:
- You find a bug – improve your tests so it never happens again
- You need to add new features – you have assurances existing behavior won't regress
- You build something nontrivial – begin by codifying your expectations (test stubs), then focus on one detail at a time.
... and then you'll be scared to do anything without it. In my 15 years of writing software, this fear is one of the most common reasons software projects fail (after market pressures).
Context
StackExchange Code Review Q#152918, answer score: 3
Revisions (0)
No revisions yet.