I am currently moving toward more of a TDD approach with my Meteor apps and want to get better at testing React components. I have found a lot of great examples, such as this one.
However, one aspect of testing React components that I am struggling with is that of testing callbacks of a child component to a parent component.
My question
What is an effective way of testing internal React component communication, such as callbacks to parent components? Thanks in advance for any tips and advice!
Example
Please find a working repo of the code below here: https://github.com/andersr/meteor-react-component-test
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({
getDefaultProps() {
return {
componentBeingTested: null
};
},
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 test looks like this. The test passes. However, it seems like there should be a simple 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);
});
});