react: ReactTestRenderer doesn't work with refs or ReactDOM.findDOMNode
Jest snapshot testing uses ReactTestRenderer but if my component contains a ref or uses ReactDOM.findDOMNode it throws TypeError: component.getPublicInstance is not a function.
Component
import React from 'react';
export default class Link extends React.Component {
render() {
return (
<a
ref={a => this._a = a}
href={this.props.page || '#'}>
{this.props.children}
</a>
);
}
}
Test
'use strict'
import React from 'react';
import Link from '../Link';
import renderer from 'react-test-renderer';
describe('Link', () => {
it('renders correctly', () => {
const tree = renderer.create(
<Link page="foo" />
).toJSON();
expect(tree).toMatchSnapshot();
});
});
stack trace
FAIL __tests__/Link-test.js (2.148s)
● Link › it renders correctly
- TypeError: component.getPublicInstance is not a function
at attachRef (node_modules/react/lib/ReactRef.js:20:19)
at Object.ReactRef.attachRefs (node_modules/react/lib/ReactRef.js:42:5)
at attachRefs (node_modules/react/lib/ReactReconciler.js:26:12)
at CallbackQueue._assign.notifyAll (node_modules/react/lib/CallbackQueue.js:67:22)
at ReactTestReconcileTransaction.ON_DOM_READY_QUEUEING.close (node_modules/react/lib/ReactTestReconcileTransaction.js:37:26)
at ReactTestReconcileTransaction.Mixin.closeAll (node_modules/react/lib/Transaction.js:204:25)
at ReactTestReconcileTransaction.Mixin.perform (node_modules/react/lib/Transaction.js:151:16)
at batchedMountComponentIntoNode (node_modules/react/lib/ReactTestMount.js:61:27)
at ReactDefaultBatchingStrategyTransaction.Mixin.perform (node_modules/react/lib/Transaction.js:138:20)
at Object.ReactDefaultBatchingStrategy.batchedUpdates (node_modules/react/lib/ReactDefaultBatchingStrategy.js:63:19)
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Comments: 18 (4 by maintainers)
Commits related to this issue
- Upgrade dependencies to fix build breakages (#238) * Upgrade dependencies to fix build breakages We've had some lint-related build failures: https://travis-ci.org/graphql/graphiql/builds/1814... — committed to graphql/graphiql by wincent 8 years ago
- Mock rc-select due to findDOMNode not being supported https://github.com/facebook/react/issues/7371 — committed to busyorg/busy by Sekhmet 7 years ago
- Replace string ref with callback ref to support jest snapshots See https://github.com/facebook/react/issues/7371 — committed to ubergrape/grape-browser by glennreyes 6 years ago
This is not a bug. As explained above it is intentional.
The workaround is simple if you use jest. Just mock the third party component causing issues.
For example:
Put this at the top of your test file.
Now any imports of
third-party-button(replace this with your component) will become a string (e.g.ThirdPartyButton) so the component will become a “leaf” in the snapshot, like adiv. Of course this won’t actually test it, but it makes sense to only test your own code.For libraries exporting multiple components, the workaround is similar but you’d want to provide a different mock. Something like:
Finally, as last option, you can mock
react-domitself.Note you can return anything there and adjust it to match the expectations of the tested code.
All of this works if you use Jest. Unfortunately I don’t have good solutions for other runners.
Mocking refs and canvas was easy using:
Is there any idea how to overcome this bug? I have few third party components (like https://github.com/ericgio/react-bootstrap-typeahead/) which uses
ReactDOM.findDOMNodehow could I use jest snapshots with them?Refs work with test renderer in master, and you can even mock them to return something else instead of null:
There are no plans to support
findDOMNode()in test renderer because it should be agnostic of React DOM and there is no way to implement it in a way that won’t break in the future.What is the recommended heuristic to detect that
findDOMNodewon’t work, in order to take a different code path? (try / catch could work but isn’t very clear)Edit: to elaborate: this is for a component library, so mocking (inside the library at least) is not an option
@romanoff
React test renderer is not coupled to React DOM. It can’t “guess” which DOM APIs your component relies on. You need to mock the nodes yourself, as noted in 15.4.0 release notes. I hope this helps!
Thanks for the well documented issue! I’ll leave it to @spicyj since he did the hard work on this.
I don’t think providing a mock DOM node would be the best solution here. We’d have to implement the full DOM node API so that when it’s actually used it doesn’t throw. It would be better if we could have an optional integration with jsdom (or whatever the user chooses to use), so that we can use a full implementation of the DOM and avoid having to implement and maintain our own mock API.
It wouldn’t, but there are a few possible options:
Any solutions you prefer?