enzyme: useContext hook not working with shallow
The useContext
hook does not work with shallow
rendering and passing in context
. Are there any workarounds for now?
Current behavior
The value returned by useContext
in a function component appears to be empty when using shallow
and passing in a context
argument. (The same problem also occurs when wrapping the component in the appropriate context Provider directly and then calling .dive()
).
Here is a minimal test case, also reproduced at https://codesandbox.io/s/nice-blackwell-yz7tn in the index.spec.js
file.
import React, { useContext } from "react";
import PropTypes from "prop-types";
import Enzyme, { shallow } from "enzyme";
import Adapter from "enzyme-adapter-react-16";
Enzyme.configure({ adapter: new Adapter() });
const MyContext = React.createContext({});
export const MyComponent = () => {
const { myVal } = useContext(MyContext);
return <div data-test="my-component">{myVal}</div>;
};
it("renders the correct text", () => {
MyComponent.contextTypes = {
myVal: PropTypes.any
};
const wrapper = shallow(
<MyComponent />,
{context: {myVal: 'foobar'}}
);
expect(wrapper.text()).toEqual("foobar"); // expected "foobar" received ""
});
Expected behavior
The text in the example above is expected to be "foobar"
, but it’s actually ""
.
In general, the value returned from useContext
appears to be undefined.
Note that using mount
instead of shallow
causes the test to pass.
Also note: the codesandbox above has a second file (class.spec.js
) in which a hack is employed that makes the test pass, which uses the legacy contextTypes
. But this only appears to work with classes and this.context
, not with useContext
.
Your environment
enzyme 3.10.0 enzyme-adapter-react-16 1.14.0 react 16.8.6 react-dom 16.8.6
API
- shallow
- mount
- render
Version
library | version |
---|---|
enzyme | |
react | |
react-dom | |
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: open
- Created 5 years ago
- Reactions: 46
- Comments: 36 (10 by maintainers)
Commits related to this issue
- WithContactDetailsValidation Unit Test: pass Redux store as wrappingComponent, use full mount React Redux 7.x no longer supports `store` prop for `Provider`, and uses the new `React.createContext()` ... — committed to Automattic/wp-calypso by jsnajdr 5 years ago
- WithContactDetailsValidation Unit Test: pass Redux store as wrappingComponent, use full mount React Redux 7.x no longer supports `store` prop for `Provider`, and uses the new `React.createContext()` ... — committed to Automattic/wp-calypso by jsnajdr 5 years ago
- WithContactDetailsValidation Unit Test: pass Redux store as wrappingComponent, use full mount React Redux 7.x no longer supports `store` prop for `Provider`, and uses the new `React.createContext()` ... — committed to Automattic/wp-calypso by jsnajdr 5 years ago
- Upgrade React Redux and Redux Form (#37438) * fix(deps): update dependency redux-form to v8 * fix(deps): update dependency react-redux to v7 * ImageEditorCanvas: change react-redux withRef to f... — committed to Automattic/wp-calypso by jsnajdr 5 years ago
- Fix RadioButton test The tests with context were failing with the error: "Invariant Violation: Hooks can only be called inside the body of a function component". The root cause is somwhere between s... — committed to simenko/precise-ui by simenko 3 years ago
- GN-117 Convert unit test from shallow to mount due to issues around enzyme support of useContext 'https://github.com/enzymejs/enzyme/issues/2176' — committed to nice-digital/global-nav by johndavey72 3 years ago
- GN-117 Remove need to assert on dom (#148) * context experiments * basic context setup * tidy up debugging * uncommen event listener for expected closing behaviour * make debug clearer ... — committed to nice-digital/global-nav by johndavey72 3 years ago
- GN-115 Mega nav production ready (#145) * GN-95 Move NavLinks to own component * GN-95 Get button in if dropdown * GN-95 Skip links and click behaviour * GN-95 Skip links * GN-95 Styling ... — committed to nice-digital/global-nav by wa-rren-dev 3 years ago
I found some workarounds for shallow rendering with React.useContext, Material UI’s makeStyles, and react-redux v7.x.x. See below for some high level notes and my codesandbox for examples.
Components that consume context using React.useContext()
just.spyOn(React, 'useContext').mockImplementation((context) => 'context_value' )
to return a context value. (Thanks to @mstorus’s example above for the idea of mocking module implementations). @garyyeap, you can do this to mock useContext for code you don’t control, but that won’t help you with react-redux 7.1. See the Redux section below for the redux workaround.<Context.Provider value={myContextValue}/>
does not work.Components that consume context using <Context.Consumer />
<Context.Provider value={myContextValue}/>
works.Material UI’s makeStyles
Redux - Redux has some fancy logic for using context. It looks like you can optionally pass the store or a custom context into a connected component.
<MyConnectedComponent store={mockStore}/>
<MyConnectedComponent context={MockReduxContext}/>
It’s pretty disappointing that shallow rendering’s been broken for so long and that our options are limited to switching to mount or finding brittle workarounds like these.
The workaround I’m using for now is the one described in this article; wrap
useContext
in a function and then usejest
to mock the implementation.https://codesandbox.io/s/crimson-mountain-vo7sk
MyContext.js
index.spec.js
@twharmon, try this for named exports.
E.G.
For me,
just.spyOn(React, 'useContext').mockImplementation(() => {...} )
will work if my component hasconst ctx = React.useContext(MyContext)
but not withconst ctx = useContext(MyContext)
.I also am having this problem, and would MEGA appreciate any workarounds or fixes that might come 😃
@twharmon I don’t have to do that if I do
instead of
for the sake of explicitness, here’s an example of directly using a context provider and calling
.dive()
, which also doesn’t work:https://codesandbox.io/s/elastic-zhukovsky-w5pjc
index.spec.js
@ljharb is the reason why this variation also doesn’t work the same as you mentioned above?
I was able to workaround it the following way:
and its usage:
You can pass context value via
wrappingComponentProps
if you wantI’m having same issue with using react-redux 7.1, but I can’t change the way how react-redux use the useContext(). Is there any other workaround?
It’d be because
.dive()
needs to access the context in order to forward it along. However, if you try this, it might work: