How to mock/spy useState hook in jest?

Saher Elgendy picture Saher Elgendy · Oct 2, 2020 · Viewed 10.2k times · Source

I am trying to spy on useState React hook but i always get the test failed

This is my React component:

const Counter= () => {
    const[counter, setCounter] = useState(0);

    const handleClick=() => {
        setCounter(counter + 1);
    }

    return (
        <div>
            <h2>{counter}</h2>
            <button onClick={handleClick} id="button">increment</button>
        </div>
    )
}

counter.test.js:

it('increment counter correctlry', () => {
    let wrapper = shallow(<Counter/>);
    const setState = jest.fn();
    const useStateSpy = jest.spyOn(React, 'useState');

    useStateSpy.mockImplementation((init) => [init, setState]);
     const button = wrapper.find("button")
     button.simulate('click');
     expect(setState).toHaveBeenCalledWith(1);
})

Unfortunately this doesn't work and i get the test failed with that message:

expected 1
Number of calls: 0

Answer

diedu picture diedu · Oct 2, 2020

You need to use React.useState instead of the single import useState.

I think is about how the code gets transpiled, as you can see in the babel repl the useState from the single import ends up being different from the one of the module import

_react.useState // useState
_react.default.useState // React.useState;

So you spy on _react.default.useState but your component uses _react.useState. It seems impossible to spyOn a single import since you need the function to belong to an object, here is a very extensive guide that explains the ways of mocking/spying modules https://github.com/HugoDF/mock-spy-module-import

And as @Alex Mackay mentioned, you probably want to change your mindset about testing react components, moving to react-testing-library is recommended, but if you really need to stick to enzyme you don't need to go that far as to mock react library itself