Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mock/spy useState hook in jest?

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
like image 518
Saher Elgendy Avatar asked Oct 02 '20 01:10

Saher Elgendy


People also ask

How do you mock useState in jest test?

To enable us to mock useState, we use React. useState (line #5) instead of using the usual named import (i.e. import { useState } from 'react'). Below is our Jest unit test for the component. Before rendering the component for testing, we create a constant 'setStateMock' and mock it with a jest function jest.

What is jest spyOn?

Jest's spyOn method is used to spy on a method call on an object. It is also very beneficial in cases where the Jest mock module or mock function might not be the best tool for the job on hand. While writing unit tests you only test one particular unit of code, generally a function.

What is useState hook used for?

The useState Hook can be used to keep track of strings, numbers, booleans, arrays, objects, and any combination of these! We could create multiple state Hooks to track individual values.

How do you spy a function in jest?

To spy on an exported function in jest, you need to import all named exports and provide that object to the jest. spyOn function. That would look like this: import * as moduleApi from '@module/api'; // Somewhere in your test case or test suite jest.

How to mock an import in jest?

In order to mock an import you need to call on the mock function from the jest object and then return an object that matches the exports. In the below example an object is returned containing the exported usePageClass as a mock function:

How do I spy on an object in jest?

Jest spies are instantiated using jest.spyOn (obj, 'functionName'). Note: you can’t spy something that doesn’t exist on the object. jest.toBeCalled () and jest.toHaveBeenCalled () are aliases of each other.

How to return a jest mock from React state?

Mock use state from react to return a jest.fn() as useState: 1.1 Also import useState immediately after - that will now be e jest mock (returned from the jest.fn() call)

Can I add additional hooks to a jest mock?

Now any additional hook (s) that needs to be mocked out can be added to the jest.mock and any that do not will be handled by the requireActual function. Note: I wrote a Jest cheatsheet for React developers that you may also find useful here.


Video Answer


2 Answers

diedu's answer led me the right direction and I came up with this solution:

  1. Mock use state from react to return a jest.fn() as useState: 1.1 Also import useState immediately after - that will now be e jest mock (returned from the jest.fn() call)

jest.mock('react', ()=>({
  ...jest.requireActual('react'),
  useState: jest.fn()
}))
import { useState } from 'react';
  1. Later on in the beforeEach, set it to the original useState, for all the cases where you need it to not be mocked

describe("Test", ()=>{
  beforeEach(()=>{
    useState.mockImplementation(jest.requireActual('react').useState);
    //other preperations
  })
  //tests
})
  1. In the test itself mock it as needed:

it("Actual test", ()=>{
  useState.mockImplementation(()=>["someMockedValue", someMockOrSpySetter])
})

Parting notes: While it might be conceptually somewhat wrong to get your hands dirty inside the "black box" one is unit testing, it is indeed super useful at times to do it.

like image 158
doni Avatar answered Sep 29 '22 16:09

doni


just you need to import React in your test file like:

import * as React from 'react';

after that you can use the mock function.

import * as React from 'react';

:
:
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);
})
like image 32
William Penagos Avatar answered Sep 29 '22 16:09

William Penagos