withRouter
is a handy high-order component from react-router
package which gives you access to match
and history
props which you can use for performing navigation inside your React application. If you want to test React components wrapped in withRouter
you'll need the following.
Let's say you have some component:
// PollDetails.js
import React, { Fragment } from 'react'
import { withRouter } from 'react-router'
import { Link } from 'react-router-dom'
const PollDetails = ({ match, history }) => {
return (
<Fragment>
<header>Question Detail</header>
<div>No question found</div>
<Link to={`/`}>
<button>Go back</button>
</Link>
</Fragment>
)
}
export default withRouter(PollDetails)
Trying to render withRouter
component with the enzyme mount
will result in an error
// PollDetails.spec.js
import React from 'react'
import { mount } from 'enzyme'
import PollDetails from './PollDetails'
let component
beforeEach(() => {
component = mount(<PollDetails />)
})
it('should render page heading.', () => {
expect(component.text()).toContain('Question Detail')
})
it('should render "no question found" message if question is not present.', () => {
expect(component.text()).toContain('No question found')
})
will lead to Invariant and a very common error that you might see working withRouter
HOC
Invariant failed: You should not use <withRouter(PollDetails) /> outside a <Router>
Using shallow
rendering
// PollDetails.spec.js
import React from 'react'
import { shallow } from 'enzyme'
import PollDetails from './PollDetails'
let component
beforeEach(() => {
component = shallow(<PollDetails />)
})
it('should render page heading.', () => {
expect(component.text()).toContain('Question Detail')
})
it('should render "no question found" message if question is not present.', () => {
expect(component.text()).toContain('No question found')
})
The error would to away but you asserting against the wrong component and all the assertions will fail:
Fixing the error by accessing WrappedComponent
By using PollDetails.WrappedComponent
we skip our withRouter
HOC and access directly component that we want to be rendered so that we can run our assertions on it.
// PollDetails.spec.js
import React from 'react'
import { shallow } from 'enzyme'
import PollDetails from './PollDetails'
let component
beforeEach(() => {
component = shallow(<PollDetails.WrappedComponent />)
})
it('should render page heading.', () => {
expect(component.text()).toContain('Question Detail')
})
it('should render "no question found" message if question is not present.', () => {
expect(component.text()).toContain('No question found')
})
Enzyme full component rendering using mount with MemoryRouter
What if you still want to use full rendering with enzyme mount?
You still can do that by wrapping your component in <MemoryRouter></MemoryRouter>
.
// PollDetails.spec.js
import React from 'react'
import { mount } from 'enzyme'
import { MemoryRouter } from 'react-router-dom'
import PollDetails from './PollDetails'
let component
beforeEach(() => {
component = mount(
<MemoryRouter>
<PollDetails />
</MemoryRouter>
)
})
it('should render page heading.', () => {
expect(component.text()).toContain('Question Detail')
})
it('should render "no question found" message if question is not present.', () => {
expect(component.text()).toContain('No question found')
})