Set state when testing functional component with useState() hook
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]);
Related videos on Youtube
Anna
Updated on July 09, 2022Comments
-
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 withuseState()
hook?For example in my component I have:
const [mode, setMode] = useState("my value");
And I want to change
mode
inside my test -
Anna about 5 yearsOh.. so we cannot test state when we use hooks
-
Moti Azu about 5 yearsNot 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 about 5 yearsHm, I guess it is true that tests actually should not rely on state changing, but should test the appearance
-
Moti Azu about 5 yearsAppearance 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 about 4 yearsSo 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. over 3 yearsThe 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 about 3 yearswhat about cases with 2 useState?
-
Doug Moses about 3 yearsIn cases with 2 or more useStates, we used mockImplementationOnce with some success but I will warn you that it wasn't pretty.