- Intro, why this topic, disclaimer
- note about TDD versus traditional dev
- Show an example of a complex view component
- Points why it is difficult to work with
- Walk through breaking it up into smaller components
- If you need helper functions - make them pure!
- React component philosophy
- stateless when possible
- as little business logic in the view layer as possible
- try not to couple views to store
- Transition to testing them
- Example of insufficient test
- Test choices & philosophies
expect
vs.assert
- sinon + chai + mocha + enzyme
- advantages of enzyme over
ReactTestUtils
- How to write tests? 2 steps
- Look at your component and think about every element of logic involved
- Think of edge cases
- Questions to ponder
say the store sets some state key like so
this.currentState = {
someArray = data.someArray || []
};
And the view does something like
render() {
// is this fallback necessary??
const someArray = this.props.someArray || [];
return (
<div>
{someArray.map(item => <div>{item.name}</div>)}
</div>
);
}
Is the fallback necessary?
- https://github.com/CondeNast/autopilot-ad/blob/master/lib/app/views/pages/article.jsx#L776
- make it PURE & TESTABLE
https://hashnode.com/post/component-rendering-performance-in-react-ciqti6xlz06n49l53t2c2g3ri
// some consumer view render
<MyComponent isSpecial={true} />
// <MyComponent> render
{this.props.isSpecial && <div>I am special</div>}
consider instead:
// some consumer view render
<MySpecialComponent />
// MySpecialComponent render
<MyComponent isSpecial={true} />
// another consumer view render
<MyUnspecialComponent />
// MyUnspecialComponent render
<MyComponent />
<MyComponent isSpecial={true} />
// internally wrap an instance of MyComponent
<MySpecialComponent />
<MyUnspecialComponent />
render() {
return (
<div className="my-component">
{this.props.title && <h1 className="my-component-title">
{this.props.title}
</h1>
}
<div className="my-component-byline">
<div className="my-component-byline-author">
{`By ${this.props.author}`}
</div>
<div className="my-component-byline-date">
{this._formatDate(this.props.publishDate)}
</div>
</div>
<article className="my-component-content-body">
{this._getContent(this.props.content)}
</article>
</div>
);
}
render() {
return (
<div className="my-component">
<MyComponentTitle title={this.props.title} />
<MyComponentByline
author={this.props.author}
publishDate={this.props.publishDate} />
<MyComponentContent content={this.props.content} />
</div>
);
}
_isSuperSpecial() {
if (!this.props.isSpecial || _.isEmpty(this.props.foo) {
return false;
}
return this.props.foo.bar
&& this.props.foo.bar > MAGIC_NUMBER;
}
_isSuperSpecial(isSpecial, foo, magicNumber) {
if (!isSpecial || _.isEmpty(foo)) {
return false;
}
return foo.bar && foo.bar > magicNumber;
}
example: AD nav / subnav: https://github.com/CondeNast/autopilot-ad/blob/7c6b95663ca9667b69e11b4a742e88d06ad6ed56/lib/app/views/components/Navigation.jsx#L63
- export each individual component as well as the default export
- set up a command to test an individual file without linting, coverage rpt
- useful links
- most useful enzyme methods / gotchas