I am trying to determine how best to mock Providers when Jest testing with React Context. The most promising lead I've found is RTL, which provides the following example in their docs: https://testing-library.com/docs/example-react-context
I have been experimenting with multiple structures, but even when I follow their example closely I get the above error.
Here I am following their example almost exactly when testing my Dialog component:
Jest Test
import React from 'react';
import { render } from '@testing-library/react';
import { DialogContext, defaultState } from '../../../../store/contexts/dialogContext';
const customRender = (ui, { providerProps, ...renderOptions }) => {
return render(
<DialogContext.Provider {...providerProps}>{ui}</DialogContext.Provider>,
renderOptions
);
};
it('Dialog should work', async () => {
const providerProps = {
dialogType: 'CANCEL',
...defaultState,
};
const { container } = customRender(<Dialog />, { providerProps });
const results = await axe(container);
expect(results).toHaveNoViolations();
});
Dialog Component
import React, { useContext } from 'react';
import { DialogContext } from '../../../store/contexts/dialogContext';
export default function Dialog() {
const [dialogState, dialogDispatch] = useContext(DialogContext);
//logic
return (
<div id='dialog'/>
);
}
This is a simplified version of my Dialog Component, but I receive the following error as soon as it invokes useContext() on line 2.
Error: Uncaught [TypeError: (0 , _react.useContext) is not a function or its return value is not iterable]
As 'React' is in scope in the relevant files, and I'm following the RTL example closely, I'm stumped. Solutions or recommendations for other ways to mock Providers welcome.
EDIT: Adding the dialogContext file for clarity. The component and the context work fine when not in the Jest flow.
import React, { createContext, useReducer } from 'react';
import { dialogReducer } from '../reducers/dialogReducer';
export const DialogContext = createContext({});
const { Provider } = DialogContext;
export const defaultState = {
//state object
}
export const DialogProvider = ({ children }) => {
const [state, reducer] = useReducer(dialogReducer, defaultState);
return <Provider value={[state, reducer]}>{children}</Provider>
}
and when DialogContext is logged from within the Dialog component (the line before), it looks as expected:
{ '$$typeof': Symbol(react.context),
_calculateChangedBits: null,
_currentValue: undefined,
_currentValue2: {},
_threadCount: 0,
Provider: { '$$typeof': Symbol(react.provider), _context: [Circular] },
Consumer:
{ '$$typeof': Symbol(react.context),
_context: [Circular],
_calculateChangedBits: null },
_currentRenderer: {},
_currentRenderer2: null }
The error is ambiguous and can really occur when useContext
is undefined and when useContext
is a function but doesn't return an iterable (array).
The first situation can happen if Jest was incorrectly configured and react
package is imported as CommonJS module that is translated to ES module default
import via module interop.
The second situation is the most simple explanation for the error. Context provider wasn't provided with array value so useContext
doesn't return an array. Unless defaultState
object contains value
pro...perty with array value, ...defaultState
spread will result in wrong provider value.
Provider value should be provided in tests the same way it was in the app:
<Provider value={[state, reducer]}>...
If the intention is to not use dialogReducer
in the test, a spy can be provided instead:
<DialogContext.Provider value={[defaultState, mockedDispach]}>...
Otherwise DialogProvider
can be used instead of DialogContext.Provider
because it already provides expected provider value.