react-router: Rendering component wrapped in `withRouter` throws when no Router is present
Version
4.0.0, 4.0.0-beta.8
Test Case
import React from 'react' // 15.4.2
import renderer from 'react-test-renderer' // 15.4.2
import { withRouter } from 'react-router'
const Component = () => <div />
const WrappedComponent = withRouter(Component)
it('will render', () => expect(renderer.create(<Component />)).toBeDefined())
it('will fail', () => renderer.create(<WrappedComponent />))
Steps to reproduce
Run above tests in Jest, or any other test runner.
Expected Behavior
Rendering WrappedComponent with react-test-renderer does not fail.
Actual Behavior
● will fail
TypeError: Cannot read property 'route' of undefined
at Route.computeMatch (node_modules/react-router/Route.js:66:22)
at new Route (node_modules/react-router/Route.js:43:20)
at node_modules/react-test-renderer/lib/ReactCompositeComponent.js:295:18
at measureLifeCyclePerf (node_modules/react-test-renderer/lib/ReactCompositeComponent.js:75:12)
at ReactCompositeComponentWrapper._constructComponentWithoutOwner (node_modules/react-test-renderer/lib/ReactCompositeComponent.js:294:16)
at ReactCompositeComponentWrapper._constructComponent (node_modules/react-test-renderer/lib/ReactCompositeComponent.js:280:21)
at ReactCompositeComponentWrapper.mountComponent (node_modules/react-test-renderer/lib/ReactCompositeComponent.js:188:21)
at Object.mountComponent (node_modules/react-test-renderer/lib/ReactReconciler.js:46:35)
at ReactCompositeComponentWrapper.performInitialMount (node_modules/react-test-renderer/lib/ReactCompositeComponent.js:371:34)
at ReactCompositeComponentWrapper.mountComponent (node_modules/react-test-renderer/lib/ReactCompositeComponent.js:258:21)
at Object.mountComponent (node_modules/react-test-renderer/lib/ReactReconciler.js:46:35)
at ReactCompositeComponentWrapper.performInitialMount (node_modules/react-test-renderer/lib/ReactCompositeComponent.js:371:34)
at ReactCompositeComponentWrapper.mountComponent (node_modules/react-test-renderer/lib/ReactCompositeComponent.js:258:21)
at Object.mountComponent (node_modules/react-test-renderer/lib/ReactReconciler.js:46:35)
at mountComponentIntoNode (node_modules/react-test-renderer/lib/ReactTestMount.js:55:31)
at ReactTestReconcileTransaction.perform (node_modules/react-test-renderer/lib/Transaction.js:140:20)
at batchedMountComponentIntoNode (node_modules/react-test-renderer/lib/ReactTestMount.js:69:27)
at ReactDefaultBatchingStrategyTransaction.perform (node_modules/react-test-renderer/lib/Transaction.js:140:20)
at Object.batchedUpdates (node_modules/react-test-renderer/lib/ReactDefaultBatchingStrategy.js:62:26)
at Object.batchedUpdates (node_modules/react-test-renderer/lib/ReactUpdates.js:97:27)
at Object.render [as create] (node_modules/react-test-renderer/lib/ReactTestMount.js:125:18)
at Object.<anonymous> (src/components/Button/Button-snapshot.js:9:118)
at process._tickCallback (internal/process/next_tick.js:109:7)
Route.computeMatch (node_modules/react-router/Route.js:66:22) is
Route.prototype.computeMatch = function computeMatch(_ref, _ref2) {
var computedMatch = _ref.computedMatch,
location = _ref.location,
path = _ref.path,
strict = _ref.strict,
exact = _ref.exact;
var route = _ref2.route; // _ref2 is undefined
Passing { withRef: true } to withRouter has no effect.
This makes it hard to unit test any component that renders as one of its children a component wrapped in withRouter.
Can be worked-around by inserting a Router.
import { MemoryRouter as Router, withRouter } from 'react-router-dom' // 4.0.0
it('will pass', () => {
expect(
renderer.create(<Router><WrappedComponent /></Router>)
.toJSON()
).toEqual({children: null, props: {}, type: 'div'})
})
Thanks!
possibly related bugfix here https://github.com/ReactTraining/react-router/issues/4292
About this issue
- Original URL
- State: closed
- Created 7 years ago
- Reactions: 17
- Comments: 17 (8 by maintainers)
Because that’s how my application code uses it.
For shallow rendering a single component: Nothing, that’s what I’m doing now to work around this.
It requires me to export the unwrapped component and import that in my test, which is a mild inconvenience.
Imagine the case where you are, in a unit test, deep rendering your component under test, and it renders somewhere in its descendent tree a component that is wrapped in withRouter. How then should we just not use withRouter in that child?
Module-level mock the child to inject an unwrapped component? Gets messy quickly.
We ended up inserting a
<Router>in our tests for those cases. This isn’t ideal because it breaks some enzyme functionality that can only operate on the root rendered element.Crux of the issue is that I don’t expect
withRouterto throw in this case, and think it would be nicer if it behaved as it did after https://github.com/ReactTraining/react-router/pull/4295 was applied to fix this issue once before.Maybe I can phrase it differently: if you’re not planning on rendering a component inside a
<Router>, why are you wrapping it inwithRouter?What’s preventing you from just not using
withRouterwhen you unit test your component?There are non-testing applications. Imagine a component that is used in two component trees – one having a Router and another not.
This actually happened in our codebase just moments ago
I’ll re-open for the time being for the
refissue, but I can say that I’m about 99% sure adding functionality to components just for testing purposes isn’t going to happen.That’s the way all of our tests work here; they all render a
<Router>at the top of the component hierarchy.I get that this is really inconvenient for you, but I’m just not sure the best way to fix it. What does redux do if you render one of your
connected components without a<Provider>up top? Does it just give you no props and a warning?The testing guide covers the necessity of rendering inside of a
<Router>.I’m not sure if
react-test-rendererallows you to pass in a context object, but if it does you can use react-router-test-context to simulate the context a router would provide. While I wrote it, I actually believe that it is usually better to just use a<MemoryRouter>. From my limited experience testing it, shallow rendering does not work well with<Route>s.Also, I’m not positive, but is your reference to
withRefjust related tocomputeMatchhaving_refand_ref2as arguments? That is just a babel thing. I’m not actually sure howrefworks withwithRouterbecause I haven’t had a need to use them together. I know that some HOCs have to do some hoisting, but I haven’t seen anyone bringing up having issues with that yet.