jeudi 18 février 2016

What is a good way of testing React component callbacks to parent components?

I am currently moving toward more of a TDD approach and want to get better at testing React components. One aspect of testing React components that I am struggling with is that of testing callbacks of a child component to a parent component.

What is an effective way of testing internal React component communication, such as callbacks to parent components?

The response to this question seems to offer a possible solution, though I didn't quite understand it (eg I'm not completely familiar with how to use function chaining in a Jasmine test.)

Thanks in advance for any tips and advice!

Example

(The following example uses Meteor, though I am not necessarily looking for Meteor-specific solutions.)

Repo with the complete example.

Let's say I have a component that accepts text input and passes it via props on submit:

SingleFieldSubmit = React.createClass({
  propTypes: {
    handleInput: React.PropTypes.func.isRequired
  },
  getDefaultProps() {
    return {
      inputValue:  ""
    };
  },
  getInitialState() {
    return {
      inputValue: this.props.inputValue
    };
  },
  updateInputValue(e){
    this.setState({inputValue: e.target.value});
  },
  handleSubmit(e) {
    e.preventDefault();
    this.handleInput();
  },
  handleInput(){
    this.props.handleInput(this.state.inputValue.trim());
  },
  render() {
    return (
      <form className="single-field-submit" onSubmit={this.handleSubmit}>
        <input
          type="text"
          value={this.state.inputValue}
          onChange={this.updateInputValue}
        />
      </form>
    )
  }
});

Here, I want to test if the component passes the user input on submit. My currently, somewhat clunky, solution is to create a mock parent component, with the component I want to test included as a child:

MockParentComponent = React.createClass({
  getInitialState: function() {
    return {
      callbackValue: null
    };
  },
  handleCallback: function(value) {
    this.setState({callbackValue: value});
  },
  render: function() {
    return (
      <div className="container">
        <SingleFieldSubmit handleInput={this.handleCallback} />
      </div>
    )
  }
});

Then, my (Jasmine) test looks like this. The test passes. However, it seems like there should be a simpler way of doing this....

describe('SingleFieldSubmit Component', function () {

  it('should, on submit, return the value input into the form', function () {

    //SETUP
    let mockUserInput = 'Test input';
    let parentComponent = TestUtils.renderIntoDocument(
          React.createElement(MockParentComponent)
          );
    let node     = ReactDOM.findDOMNode(parentComponent);
    let $node    = $(node);
    expect(parentComponent.state.callbackValue).toBe(null);

    //TEST
    Simulate.change($node.find('input')[0], { target: { value: mockUserInput } });
    Simulate.submit($node.find('form')[0]);
    expect(parentComponent.state.callbackValue).toBe(mockUserInput);
  });

});

Aucun commentaire:

Enregistrer un commentaire