Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Set state when testing functional component with useState() hook

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

like image 379
Anna Avatar asked Mar 25 '19 16:03

Anna


3 Answers

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')
})
like image 156
Moti Azu Avatar answered Oct 19 '22 21:10

Moti Azu


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.

like image 11
Doug Moses Avatar answered Oct 19 '22 21:10

Doug Moses


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]);

like image 5
Furkan Uyar Avatar answered Oct 19 '22 21:10

Furkan Uyar