React, how to simulate user input for unit testing?

27,921

You seem to have a bug in your Input component. When event.target.value.length > this.props.maxLength you never set the actual state, leaving the state.value as ''. It seems you expected it to have been set to the value, but truncated to maxLength. You'll need to add that yourself:

handleChange(event) {
  /* Check if max length has been set. If max length has been
  set make sure the user input is less than max Length, otherwise
  return before updating the text string. */
  if (this.props.maxLength) {
    if (event.target.value.length > this.props.maxLength) {
      // ** Truncate value to maxLength
      this.setState({ value: event.target.value.substr(0, this.props.maxLength) });
      return;
    }
  }
  this.setState({ value: event.target.value });
}

... then, the following test works and passes:

it('Make sure inputted text is shorter than max length', () => {
  const result = mount(<Input maxLength={10}></Input>);
  result.find('input').simulate('change', { target: { value: '1234567890!!!' } });
  expect(result.state().value).to.equal("1234567890");
});
Share:
27,921
2trill2spill
Author by

2trill2spill

Fuzzing, FreeBSD, MacOS, C, Golang, Node.js, and React.

Updated on July 16, 2022

Comments

  • 2trill2spill
    2trill2spill almost 2 years

    I have been trying to unit test a react component that takes in user input. More specifically I'm trying to test the onChange function within the react component. However I can't seem to set the input value, I've tried a few different ways suggested on the internet and none seem to work. Below is the component I'm trying to test.

    class Input extends Component {
      constructor(props) {
        super(props);
        this.state = {value: ''};
        this.handleChange = this.handleChange.bind(this);
      }
    
      handleChange(event) {
        /* Check if max length has been set. If max length has been
        set make sure the user input is less than max Length, otherwise
        return before updating the text string. */
        if(this.props.maxLength) {
          if(event.target.value.length > this.props.maxLength) {
            return;
          }
        }
        this.setState({ value: event.target.value });
      }
    
      render () {
        const { disabled, label, maxLength, multiline, type, value, ...others} = this.props;
        const theme = themeable(others.theme);
    
        let inputClassName = classNames({
          "input": type !== 'checkbox',
          "checkbox": type == 'checkbox',
          disabled,
          multiline,
          value,
          [`${this.props.className}`]: !!this.props.className
        });
    
        return (
          <div {...theme(1, 'container')}>
            {this.props.label ? <label htmlFor={this.props.htmlFor} {...theme(2, 'label')}>{label}</label> : null}
              <input value={this.state.value} {...theme(3, ...inputClassName)} onChange={this.handleChange} type={type} />
          </div>
        );
      }
    }
    

    I found this issue: https://github.com/airbnb/enzyme/issues/76 and tried the suggestions towards the bottom, I keep getting either undefined or a blank string. I tried levibuzolic's suggestion of using enzyme's simulate change, which can be seen below. However this just returns AssertionError: expected '' to equal 'abcdefghij'

    it('Make sure inputted text is shorter than max length', function() {
        const result = mount(<Input maxLength={10}></Input>);
        result.find('input').simulate('change', {target: {value: 'abcdefghijk'}});
        expect(result.state().value).to.equal("abcdefghij");
    });
    

    Then I tried takkyuuplayer's suggestion which is also below. This also fails with AssertionError: expected '' to equal 'abcdefghij'

      it('Make sure inputted text is shorter than max length', function() {
        const result = mount(<Input maxLength={10}></Input>);
        result.find('input').node.value = 'abcdefghijk';
        expect(result.state().value).to.equal("abcdefghij");
      });
    

    I found this article: https://medium.com/javascript-inside/testing-in-react-getting-off-the-ground-5f569f3088a#.f4gcjbaak and tried their way which also failed.

      it('Make sure inputted text is shorter than max length', function() {
        const result = mount(<Input maxLength={10}></Input>);
        let input = result.find('input');
        input.get(0).value = 'abcdefghijk';
        input.simulate('change');
        expect(result.state().value).to.equal("abcdefghij");
      });
    

    Finally I tried using the react test utils as suggested by Simulating text entry with reactJs TestUtils, below is the code I tried, however this failed with the error message: TypeError: Cannot read property '__reactInternalInstance$z78dboxwwtrznrmuut6wjc3di' of undefined

      it('Make sure inputted text is shorter than max length', function() {
        const result = mount(<Input maxLength={10}></Input>);
        let input = result.find('input');
        TestUtils.Simulate.change(input, { target: { value: 'abcdefghijk' } });
        expect(result.state().value).to.equal("abcdefghij");
      });
    

    So how does one simulate user input so they can test the onChange function?

  • umesh shakya
    umesh shakya over 4 years
    how can web write test case without using state?