Set state when testing functional component with useState() hook

68,638

Solution 1

When using state from hooks, your test must ignore implementation details like state in order to properly test it. You can still make sure the component passes the correct state into its children.

You can find a great example in this blog post written by Kent C. Dodds.

Here's an excerpt from it with a code example.

Test that relies on state implementation details -

test('setOpenIndex sets the open index state properly', () => {
  const wrapper = mount(<Accordion items={[]} />)
  expect(wrapper.state('openIndex')).toBe(0)
  wrapper.instance().setOpenIndex(1)
  expect(wrapper.state('openIndex')).toBe(1)
})

Test that does not rely on state implementation details -

test('counter increments the count', () => {
  const {container} = render(<Counter />)
  const button = container.firstChild
  expect(button.textContent).toBe('0')
  fireEvent.click(button)
  expect(button.textContent).toBe('1')
})

Solution 2

This is the way that I found to do it, I'm not saying this is right or wrong. In my case, a block of code was dependent on state being set to a particular value. I will keep my opinions about testing in React to myself.

In your test file: Adjust your import for the react library

import * as React from 'react'

Then in your test spy on useState and mock its implementation

const stateSetter = jest.fn()
jest
.spyOn(React, 'useState')
//Simulate that mode state value was set to 'new mode value'
.mockImplementation(stateValue => [stateValue='new mode value', stateSetter])

Please be aware that mocking useState this will way apply to all instances where useState is called for your test, so if you have more than one state value that you are looking at, they will all be set to 'new mode value'. Someone else may be able to help you sort that out. Hope it helps.

Solution 3

At top of test file, can be defined first as:

  import { useState } from 'react';

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

  const useStateMock: jest.Mock<typeof useState> = useState as never;

After that at each test can be used with different value which is wanted to be tested:

  const setValue = jest.fn();
  useStateMock
    .mockImplementation(() => ['value', setValue]);

Share:
68,638

Related videos on Youtube

Anna
Author by

Anna

Updated on July 09, 2022

Comments

  • Anna
    Anna almost 2 years

    When I tested class component with enzyme I could do wrapper.setState({}) to set state. How can I do the same now, when I am testing function component with useState() hook?

    For example in my component I have:

    const [mode, setMode] = useState("my value");
    

    And I want to change mode inside my test

  • Anna
    Anna about 5 years
    Oh.. so we cannot test state when we use hooks
  • Moti Azu
    Moti Azu about 5 years
    Not directly as of now. It's hard for me to imagine how that would be possible because of the syntax of hooks relying on call order and not naming. I look at it like testing classes with no access to privates - it's nice to have the access but it usually means you could have written it better :)
  • Anna
    Anna about 5 years
    Hm, I guess it is true that tests actually should not rely on state changing, but should test the appearance
  • Moti Azu
    Moti Azu about 5 years
    Appearance is hard to test, but if you have a state it's usually passed on to a child component as a prop, that you can make sure has gotten in the way you expect it. Or even an API call mock with the state.
  • Reacting
    Reacting about 4 years
    So I can say that the famous Kent C. Dodds is really wrong. This is what happens when rely a lot on the words of programmers that only knows how to Tweet. I work for IBM and we were obligated to test the hooks. Obviously we MUST to test functionality that goes along hooks, we can not ignore the facts that they exist and are critical. I followed some guidance from this post blog.carbonfive.com/2019/08/05/… and also I will be posting and answer here soon to let you know how to REALLY test hooks and it is not like Kent said. He is just wrong.
  • Cristian E.
    Cristian E. over 3 years
    The second test is as good as an E2E test, its not an UNIT test where the consumer is the developer and not the browser
  • kuka
    kuka about 3 years
    what about cases with 2 useState?
  • Doug Moses
    Doug Moses about 3 years
    In cases with 2 or more useStates, we used mockImplementationOnce with some success but I will warn you that it wasn't pretty.