Since the enzyme can make us get easier to update to new version of React and make our test code more lean and maintainable
You'd better not use props() to get props from component, because Enzyme document said that only the root component can get props by the function. We can use chai-moment to do the assertion
Render a component
Please use methods in require('test/unit/helpers/renderHelpers') to render component, do not use enzyme directly, this gives us the flexibility to handle the memory issue.
- Currently the renderHelper.js is only on the spike branch. And will make a pr soon.
- You can also checkout this spike branch to see how we change the existing tests to enzyme.
UI component
const {mount} = require('test/unit/helpers/renderHelpers')
const wrapper = mount(<div></div>);
You can specify the DOM node you want to mount on by provide the second argument:
const container = document.createElement('div');
const wrapper = mount(<div></div>, {
attachTo: container
});
enzyme also provide shallow render, but I suggest to use mount for keeping things consistant.
Page or component with route dependency
const {mountWithRouteContext} = require('test/unit/helpers/renderHelpers')
const wrapper = mount(<YourPage/>);
Wrapper methods of enzyme
When you call the render method we metioned before, instead of return a ReactComponent, now we returns a wrapper provide by enzyme.
Here I would like to introduce some most useful methods. And please check the document for more information.
find a child component
find
the powerful find method can find a node with powerful Enzyme Selector which support CSS selector and component constructor.
const wrapper = mount(
<div>
<span className='foo'>
<span className='bar'>
<Icon type='baz'/>
</span>
</span>
</div>);
//by jQuery selector
const fooWrapper = wrapper.find('.foo')
//find also returns a wrapper, so it support chaining call
const barWrapper = wrapper.find('.foo').find('.bar')
//by Component type
const iconWrapper = wrapper.find(Icon)
ref
class Foo extends React.Component {
render() {
return (
<div>
<span ref="firstRef" amount={2}>First</span>
<span ref="secondRef" amount={4}>Second</span>
<span ref="thirdRef" amount={8}>Third</span>
</div>
);
}
}
const wrapper = mount(<Foo />);
expect(wrapper.ref('secondRef').prop('amount')).to.equal(4);
at/childAt
The wrapper in enzyme is just like jQuery object. so it might have multiple components inside, the at and childAt can find a node in the current wrapper by index passed in
Note : the index is zero-based
const wrapper = mount(
<ul>
<li/>
<li/>
<li/>
</ul>
)
//find the first li
wrapper.find('li').at(0)
wrapper.find('li').first()
//find the third li node
wrapper.find('li').at(2)
wrapper.find('li').last()
wrapper.childAt(2)
instance
You can get the ReactComponent instance, but it's only available for the wrapper of root node (the node passed into mount())
wrapper.state() in enzyme is only available for root component
It's useful when you want to stub a method like transitionTo
buttonLink.instance().transitionTo = spy();
//This is unvaliable! .instance is only for the root node
buttonLink.find('span').instance()
simulate
To simulate events:
const wrapper = mount(<div/>)
wrapper.simulate('click')
Assertion
- suggest to use chai and chai-enzyme to do the assertion instead of should.js, we've created some customized assert method like containText work with should.js before, but I think chai-enzyme cover more scenarioes.
- highly suggest you to check the document of chai-enzyme when you don't know how to do the assertion, and below are the most common cases we will meet
A node is shown
const wrapper = mount(<div><span></span></div>);
expect(wrapper.find('span')).to.be.present();
expect(wrapper.find('span')).to.exist; //exist is an alias
expect(wrapper.find('ul')).to.not.be.present();
expect(wrapper.find('ul')).to.not.exist;
Have or contain some text
const wrapper = mount(
<div id='root'>
<span id='child'>Test</span>
</div>
)
expect(wrapper.find('#child')).to.have.text('Test')
expect(wrapper.find('#child')).to.contain.text('Te')
expect(wrapper.find('#child')).to.not.have.text('Other text')
Have some prop or className
const wrapper = mount(
<div>
<ul>
<li><User index={1} user={{name: 'Jane'}} /></li>
<li><User index={2} /></li>
</ul>
</div>
)
expect(wrapper.find(User).first()).to.have.prop('index')
expect(wrapper.find(User).first()).to.not.have.prop('invalid')
expect(wrapper.find(User).first()).to.have.prop('index', 1)
expect(wrapper.find(User).first()).to.not.have.prop('index', 2)
expect(wrapper.find(User).first()).to.have.prop('index').equal(1)
expect(wrapper.find(User).first()).to.have.prop('user').deep.equal({name: 'Jane'})
And if you want to check the className, here is a better way:
const wrapper = mount(
<div className='root top'>
<span className='child bottom'>test</span>
</div>
)
expect(wrapper.find('span')).to.have.className('child')
expect(wrapper.find('span')).to.not.have.className('root')
to.be.true;
to.be.null;
to.have.lengthOf(2);
to.have.deep.property(‘response.title’, ’sdfdgd')