react-router-enzyme-context
A helper for mocking react router v4 context and childContextTypes when testing components with Enzyme mount() and shallow().
This package uses React-Router's history module to mock the history. Specifically, we use their memoryHistory module which was designed for use in testing environments.
It creates a context option and a contextChildTypes option that can be used by Enzyme's mount() and shallow() API's to ensure that all descendants of the component being tested have the correct react-router context.
Install
npm install --save-dev react-router-enzyme-context
Quick Usage Example
const options = ; const wrapper = ;});
Or...
const options = ;const wrapper = ;
Problem
Let's say you are performing a unit test on a component that uses react-router v4's Link component. When you click on the link, a react-router executes a route, and the component increments a counter that is stored in it's state.
Here is our simple example of a "dumb component" that would be placed within a "container component" that, in turn, is part of a react router route.
;;;; Component static propTypes = destination: PropTypesstringisRequired { super; thisstate = count: 0 ; thisonClick = thisonClick; } { this; } { return <div className=scsslinkWrapper> <Link className=scsslink to=thispropsdestination onClick=thisonClick > Clicked <span className=scsscount>thisstatecount<span> Times!<span> </Link> </div> }
Here is an integration test using jest and enzyme to see if the counter increments.
;;; ; ;
The test above will crash because Enzyme will complain that the Link component must be a descendant of a Router compnent. The react-router library uses React's context feature to pass data between other react-router components. Many React component libraries use context to communicate.
● <Click Me /> › should increment when clicked
Invariant Violation: You should not use <Link> outside a <Router>
You can go through the trouble of wrapping your <ClickMe>
component inside of a <Router>
. However, enzyme can only find the state of the root component.
<Router> // <--- I am now the root <ClickMe> // <--- Enzyme can't test my state or props because I'm not the root!</Router>
Solution
Enzyme lets you specify the context directly when you call mount or shallow. Both mount and shallow have a second optional argument where you can supply context and other things.
Here is an excerpt from the Enzyme documentation:
mount(node[, options]) => ReactWrapper
Arguments
- node (ReactElement): The node to render
- options (Object [optional]):
- options.context: (Object [optional]): Context to be passed into the 4. component
- options.attachTo: (DOMElement [optional]): DOM Element to attach the component to.
- options.childContextTypes: (Object [optional]): Merged contextTypes for all children of the wrapper.
Returns
- ReactWrapper: The wrapper instance around the rendered output.
When you supply the context in this manner, your <ClickMe />
component is still the root! So you can test state and props.
Here is an example using our react-router-enzyme-context module.
;;;; ; ;
Simulating props passed via withRouter() HOC
If the component you are testing is directly wrapped by react-router's withRouter() higher order component, you may want to insert the history and location props into your component.
Currently, match is not supported. You'll have to insert those manually.
; ; { const location = props ; return <div> <h1>My Location:</h1> <p>locationpathName</p> </div> };
;;;; ; ;