Yup, so the issue is because we are spying on React.useState, which sets a spy on the default export from 'react'. Since you are pulling in useState as a named export, there is no spy on it.
I've messed around with mocking the named export, but it's not super straight forward. I've always done it using import * as Something from 'module';, but I can't seem to get that to work with react.
And if you mock the whole react module, you get some errors from enzyme.
If you can figure it out, please let me know because I'm very interested :)
But it seems that just calling useState from React is the easiest way. To be honest, I don't see the point in importing useState as a named export when it's already included in the default export.
Great, thanks. I'll play around with it - we're using useState as a named export all over the place - wouldn't be a HUGE deal to convert but it'd be nice to figure out how to test as-is.
The following technique works well for me testing functional components with useState destructured. This is an adapted solution from that above because the mockImplementation above caused react-test-renderer tests to fail:
import * as React from 'react';
describe('Some message', () => {
const setState = jest.fn();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const useStateMock: any = (initState: any) => [initState, setState];
afterEach(() => {
jest.clearAllMocks();
});
it('Is a test where we want to mock useState', () => {
jest.spyOn(React, 'useState').mockImplementation(useStateMock);
const wrapper = shallow(<Component {...props} />);
// trigger setState somehow
expect(setState).toHaveBeenCalledTimes(1);
// Other tests here
});
});
This is typescript, but it should work just as well in JS if you remove the type annotations
This is super helpful, thank you. Can you shed any light on why importing
useState
:import React, { useState } from 'react'
and calling it on line 4 of FunctionalComponent.jsx as:
const [count, setCount] = useState(0)
breaks the test? Seems like the spy should still see that getting called?
Yup, so the issue is because we are spying on
React.useState
, which sets a spy on the default export from 'react'. Since you are pulling in useState as a named export, there is no spy on it.I've messed around with mocking the named export, but it's not super straight forward. I've always done it using
import * as Something from 'module';
, but I can't seem to get that to work with react.And if you mock the whole react module, you get some errors from enzyme.
Here are some of the sources I used when looking into this:
jest-mock-default-named-export
mock-spy-exported-functions-within...
If you can figure it out, please let me know because I'm very interested :)
But it seems that just calling useState from React is the easiest way. To be honest, I don't see the point in importing useState as a named export when it's already included in the default export.
Great, thanks. I'll play around with it - we're using useState as a named export all over the place - wouldn't be a HUGE deal to convert but it'd be nice to figure out how to test as-is.
Did you happen to find a solution?
to make it work with useState we would have to mock it using jest while keeping rest of react intact. jest.requireActual can be used to achieve that
The following technique works well for me testing functional components with
useState
destructured. This is an adapted solution from that above because the mockImplementation above caused react-test-renderer tests to fail:This is typescript, but it should work just as well in JS if you remove the type annotations
works fine for me
Thanks @Jonah, It works
How can we test this type of case with jest and enzyme?
const [open, setOpen] = useState(false);
const handleClose = () => {
setOpen(!open);
this handleClose is an onClick event