enzyme: Testing Hooks with shallow: Invariant Violation
Current behavior
When testing component which contains newly released React Hooks using shallow
, it crashes:
Invariant Violation: Hooks can only be called inside the body of a function component.
Everything works fine at run time or when testing with render
:
My test component:
import * as React from 'react';
function Test() {
const [myState, setMyState] = React.useState('Initial state');
const changeState = () => setMyState('State updated');
return (
<div>
{myState}
<button onClick={changeState}>Change</button>
</div>
);
}
export default Test;
My test file:
import { shallow } from 'enzyme';
import * as React from 'react';
import Test from './Test';
it('renders without crashing', () => {
const comp = shallow(<Test />);
expect(comp.find('Test')).toMatchSnapshot();
});
Error stack trace:
Invariant Violation: Hooks can only be called inside the body of a function component.
at invariant (node_modules/react/cjs/react.development.js:125:15)
at resolveDispatcher (node_modules/react/cjs/react.development.js:1450:28)
at Object.useState (node_modules/react/cjs/react.development.js:1473:20)
at Test (src/Test.tsx:4:11)
at node_modules/enzyme-adapter-react-16/build/ReactSixteenAdapter.js:440:38
at ReactShallowRenderer.render (node_modules/react-test-renderer/cjs/react-test-renderer-shallow.development.js:412:39)
at node_modules/enzyme-adapter-react-16/build/ReactSixteenAdapter.js:444:37
at withSetStateAllowed (node_modules/enzyme-adapter-utils/build/Utils.js:137:16)
at Object.render (node_modules/enzyme-adapter-react-16/build/ReactSixteenAdapter.js:443:70)
at new ShallowWrapper (node_modules/enzyme/build/ShallowWrapper.js:206:22)
at Object.shallow (node_modules/enzyme/build/shallow.js:21:10)
at Object.<anonymous> (src/Test.test.tsx:6:18)
at new Promise (<anonymous>)
at Promise.resolve.then.el (node_modules/p-map/index.js:46:16)
at process._tickCallback (internal/process/next_tick.js:68:7)
Expected behavior
Tests should run
Your environment
Fresh create-react-app-typescript install with react 16.7.0-alpha-0
API
- shallow
- mount
- render
Version
library | version |
---|---|
enzyme | 3.8.0 |
react | 16.7.0-alpha.0 |
react-dom | 16.7.0-alpha.0 |
react-test-renderer | |
adapter (below) |
Adapter
- enzyme-adapter-react-16
- enzyme-adapter-react-16.3
- enzyme-adapter-react-16.2
- enzyme-adapter-react-16.1
- enzyme-adapter-react-15
- enzyme-adapter-react-15.4
- enzyme-adapter-react-14
- enzyme-adapter-react-13
- enzyme-adapter-react-helper
- others ( )
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 39
- Comments: 79 (31 by maintainers)
Commits related to this issue
- Recreate yarn.lock to get a single new version of react-test-renderer. See https://github.com/airbnb/enzyme/issues/1938. — committed to Christof/gql-github by Christof 5 years ago
I had the same issue and the reason was multiple versions of
react-test-renderer
For some reason,
enzyme-adapter-react-16
had an oldreact-test-renderer@16.2.0
as its own dependency. I had to remove and add them back:Hooks are not yet in a non-alpha version, and are definitely not yet supported in enzyme.
I’d suggest waiting to use them until they’re not experimental.
@icyJoseph I managed to get this working by upgrading everything around react and testing:
@bdwain Hi 😃 You wrote, that you’ve been able to run a simple test with useState, I’ve alse written one simple case and unfortunately it did not work. Maybe you know what is wrong here or maybe it is some React bugs - I am not sure, and I don’t want to create new issue if it is only my mystake
// Text is still 1234 (initial state)
@icyJoseph
From what I can tell, the problem with the shallow test in https://github.com/airbnb/enzyme/issues/1938#issuecomment-467027468 is unrelated to the title of this issue (“Hooks can only be called inside the body of a function component”). This is why this issue is confusing to me.
It would be great if there were separate issues for separate problems. This particular issue (“Hooks can only be called inside the body of a function component”) has already been fixed in React.
The issue you’re seeing (state update doesn’t trigger re-render) is a bug in React shallow renderer. We’re tracking it in https://github.com/facebook/react/issues/14840.
As for Enzyme’s
setState
helper — indeed, I don’t see it working with components using Hooks. Components using Hooks may have arbitrary number of internal states. The intended testing strategy for them is that you trigger the actual interactions that call those state setters rather than call them directly.This works for now. You need to add
react-test-renderer
if it’s not already a dependency.I’m cutting 16.8.5 now. It includes the shallow rendering fixes.
I verified that Enzyme suite passes, and that applying https://github.com/airbnb/enzyme/pull/2014 on top of 16.8.5 will also fix all cases @wodenx collected in https://github.com/airbnb/enzyme/issues/1938#issuecomment-461276294 (thanks for that!)
If something’s still broken please file an issue. Thanks.
@icyJoseph Here’s a working example with
mount
. Didn’t manage to get it working withshallow
actuallyThe component Test.tsx
Test file Test.test.tsx
Hope this helps!
I know it’s been said a number of times above, but the main part of the suggested solutions for me was to force
yarn
to force theresolution
ofreact-test-renderer
to match myreact
version:So I basically ended up with:
Updating everything solved the problem for me:
"devDependencies": { "enzyme": "^3.9.0", "enzyme-adapter-react-16": "^1.4.0", "eslint": "^5.16.0", "react": "^16.8.6", "react-dom": "^16.8.6", "react-test-renderer": "^16.6.2" }
Hooks have been released and this still seems to be an issue with enzyme’s
shallow
renderer. Is there any sort of ETA on getting an update to enzyme to support hooks?Merged at facebook/react#14567
@koszatnik12 I have the same, with enzyme. I updated to lates 16.8.5 but simple unit test with
useState
does not work for me.Upgrading react-test-render as @joepuzzo said fixed my problem.
@ljharb Hooks are labeled “upcoming” now 🤔.
v3.10.0 has now been released.
You can follow the progress here (
useEffect
withshallow
): https://github.com/airbnb/enzyme/issues/2086any update?
useState
works with shallow already, thanks! butuseEffect
still behaves differently with mount and shallow:btw:
useLayoutEffect
have the same issueI get
ShallowWrapper::setState() can only be called on class components
if i try tosetState
on a functional component that uses theuseState
hook.Can somebody please create an example project demonstrating what doesn’t work? On the React side, we expect shallow rendering a component with Hooks to work just fine.
@ljharb lol i was just checking that - i just upgraded from 16.8.0 to 16.8.1 and have the same results.
@chenesan that would be great. The biggest priority is getting as many tests as possible, and mirroring those as much as possible between shallow and mount.
Fair enough, but they’re still both not yet a thing in a real version, and enzyme doesn’t yet have support for them.
This solution worked for us, but we also realised we did not need
react-test-renderer
at all, so we removed it frompackage.json
completelyTest cases with Hooks still works
updating
enzyme-adapter-react-16
to version1.15.1
fixed the problem for meI created a react issue for this
@gaearon Yea I was planning on filing an issue with React. Are you saying file the issue with enzyme instead because the react shallow renderer should not run the effects? I am worried that this can’t be solved with just enzyme, which is why I was planning on filing the issue with react.
It sounds like it would be difficult for enzyme to keep track of the effects without digging into internals of react because the effects are usually just anonymous functions inside a component (whereas in classes it can call
instance.componentDidUpdate()
or something). Similar to how keeping track of state fromuseState
is difficult since there’s not one single state object.That made it sound like it was a choice by the react team to not run the effects in the shallow renderer. That’s why I was thinking it would be nice if it exposed some option to enable running effects on an opt-in basis, where you are aware of the potential side effects. If I’m wrong and it’s a big effort to do it from react then that might change things, but it seemed like the best approach to me.
@gaearon I have this sandbox.
There you can find a Counter inside Hooks.
With these two tests.
The enzyme test fails, the one using your suggested approach passes.
I am having a possibly related issue with 16.8. I don’t get the Invariant Violation, but some hooks which persist data across renders (useRef and useState) are not behaving as expected during shallow render, but work fine during mount.
Versions:
Tests:
Results:
Reopening, since https://github.com/facebook/react/pull/14679 is now merged and v16.8 seems imminent.
Also tracked in #1553.
An open issue in react facebook/react/#14091
This are my dependencies, and I still get this error (hook should not be called outside of balblabla)
Does it matter that between my test file and the inner tested component there is a class based one? SOmething like this
Where TestedClassComponent is a class component that internally uses a functional component that depends on the provider? (which is also functional)
That worked for me too, thanks !
I was getting intermittent testing failures even after https://github.com/airbnb/enzyme/issues/1938#issuecomment-500637733 . I was able to get my specs passing consistently by doing the following:
Then using Enzyme’s
mount
(instead ofshallow
) in my failing specs (found after reading this post).@alexanderkjeldaas the next one.
It is expected that
shallow
won’t calluseEffect
— just like shallow rendering never invokedcomponentDidMount
orcomponentDidUpdate
.This is by design because code in them is usually not resilient to shallow rendering. If you want to test effects, you probably want to use
mount
(and possibly mock out some child components that you don’t want in your test).File a new issue in React repo if you think it’s a React bug. A repository with reproducing case would be very helpful.
As I already wrote earlier, state not updating is a separate bug in React. It has already been fixed in master, but the fix is not released yet. This bug has nothing to do with Enzyme. You’ll see the fix in the next React release (either 16.8.5 or 16.9.0).
@ljharb isnt that more of an alternative rather than a fix?
so it looks like my issue goes away when I upgrade react-test-renderer to the latest version. After reading more into these issues i found this https://github.com/facebook/react/pull/14567 and realized my issue was simply because i was relying on
react-test-renderer@16.4.2
. That all being said I do agree that the title of this issue is a little confusing because it seems like the case initially described in the first comment works just fine now.If it’s still an issue with every React package (including the test renderer) at 16.8, then a PR with tests would be quite welcome.
I don’t know if there’s any ETA for supporting hooks but I’ll start to work on this in recent days 😃