DEV Community

loading...

Discussion on: Testing React Hook State Changes

Collapse
theactualgivens profile image
Andrew Givens Author

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.

Collapse
casconed profile image
Jason Moore

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.

Thread Thread
squidsoup profile image
Kit Randel

Did you happen to find a solution?

Thread Thread
ppciesiolkiewicz profile image
Piotr

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

import React, { useState as useStateMock } from 'react';

jest.mock('react', () => ({
  ...jest.requireActual('react'),
  useState: jest.fn(),
}));

describe('Test', () => {
  const setState = jest.fn();

  beforeEach(() => {
    useStateMock.mockImplementation(init => [init, setState]);
  });

...
Enter fullscreen mode Exit fullscreen mode
Thread Thread
jbolotin profile image
Jonah Bolotin

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
    });
});
Enter fullscreen mode Exit fullscreen mode

This is typescript, but it should work just as well in JS if you remove the type annotations

Thread Thread
fredreis profile image
Fred-Reis

works fine for me

Thread Thread
brianle10680751 profile image
Brian Le

Thanks @Jonah, It works

Thread Thread
dhanvinarc profile image
Dhanvin Patel • Edited

How can we test this type of case with jest and enzyme?
const [open, setOpen] = useState(false);
const handleClose = () => {
setOpen(!open);

}
Enter fullscreen mode Exit fullscreen mode

this handleClose is an onClick event

Forem Open with the Forem app