The premise
I have the following components:
// providers/SomeContextProvider.tsx
import { createContext, ReactNode, useContext } from 'react';
const SomeContext = createContext('');
export type Props = {
children: ReactNode;
};
export default function SomeContextProvider(props: Props) {
const [someCalculatedValue, setSomeCalculatedValue] = useState();
useEffect(() => {
//calculate and set the someCalculatedValue
}, [])
return (
<SomeContext.Provider value={someCalculatedValue}>
{props.children}
</SomeContext.Provider>
);
}
export function useSomeContext() {
return useContext(SomeContext);
}
// SomeConsumer.tsx
import { useSomeContext } from 'providers/SomeContextProvider'
return function SomeConsumer() {
const contextValue = useSomeContext();
return (/***/);
}
I want to test SomeConsumer
in a way that I can control the "computed value" that it gets from useSomeContext()
.
In order to do that I am mocking the SomeContextProvider.tsx
file by creating a providers/__mocks__/SomeContextProvider.tsx
(as per Vitest docs)
//providers/__mocks__/SomeContextProvider.tsx
import { createContext, ReactNode, useContext } from 'react';
const SomeContext = createContext('');
export type Props = {
children: ReactNode;
testValue: string; // Notice the mock provider accepts the value that it will pass down, as a prop
};
export default function SomeContextProvider(props: Props) {
return (
<SomeContext.Provider value={props.testValue}>
{props.children}
</SomeContext.Provider>
);
}
export function useSomeContext() {
return useContext(SomeContext);
}
and then mocking the module in the test file:
// SomeConsumer.test.tsx
import SomeConsumer from 'SomeConsumer.tsx';
import SomeContextProvider from 'providers/SomeContextProvider'
import { customRender } from 'test/utils';
vi.mock('providers/SomeContextProvider');
describe('SomeConsumer', function () {
it('should match snapshot', () => {
const { asFragment } = customRender(
<SomeContextProvider testValue={'someValue}>
<SomeConsumer>{/***/}</SomeConsumer>
</SomeContextProvider>
);
expect(asFragment()).toMatchSnapshot();
});
});
The problem
The problem is that while the mocked SomeContextProvider
is indeed called, can confirm this by setting breakpoints, SomeConsumer
is still calling the useSomeContext
exported from the original providers/SomeContextProvider.tsx
and not the one exported from providers/__mocks__/SomeContextProvider.tsx
and as a result this is not working as intended.
The question
How can I make it so that when testing SomeConsumer
it is using the mocked useSomeContext
that is exported from the mocked SomeContextProvider.tsx
file?
Well, nevermind, this seems to work fine after all. Turns out I had forgotten a doMock
instead of mock
there, while I was testing this. All's well