HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Unit tests for React component to submit an input form with validation

Submitted by: @import:stackexchange-codereview··
0
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

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:

  • should have a ` element



  • element should have a onSubmit attribute



  • onSubmit attribute should be of type function



  • element should have an element



  • element should be of type text



  • 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: true

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:

  • 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.