[英]How to set initial state for useState Hook in jest and enzyme?
Currently Im using functional component with react hooks.目前我正在使用带有反应挂钩的功能组件。 But I'm unable to test the
useState
hook completely.但是我无法完全测试
useState
挂钩。 Consider a scenario like, in useEffect
hook I'm doing an API call and setting value in the useState
.考虑这样的场景,在
useEffect
挂钩中,我正在调用 API 并在useState
中设置值。 For jest/enzyme I have mocked data to test but I'm unable to set initial state value for useState
in jest.对于玩笑/酶,我模拟了要测试的数据,但我无法在玩笑中为
useState
设置初始 state 值。
const [state, setState] = useState([]);
I want to set initial state as array of object in jest.我想开玩笑地将初始 state 设置为 object 的数组。 I could not find any setState function as similar like class component.
我找不到任何 setState function 与 class 组件类似。
You can mock React.useState
to return a different initial state in your tests:你可以模拟
React.useState
在你的测试中返回一个不同的初始状态:
// Cache original functionality
const realUseState = React.useState
// Stub the initial state
const stubInitialState = ['stub data']
// Mock useState before rendering your component
jest
.spyOn(React, 'useState')
.mockImplementationOnce(() => realUseState(stubInitialState))
Reference: https://dev.to/theactualgivens/testing-react-hook-state-changes-2oga参考: https : //dev.to/theactualgivens/testing-react-hook-state-changes-2oga
First, you cannot use destructuring in your component.首先,您不能在组件中使用解构。 For example, you cannot use:
例如,您不能使用:
import React, { useState } from 'react';
const [myState, setMyState] = useState();
Instead, you have to use:相反,您必须使用:
import React from 'react'
const [myState, setMyState] = React.useState();
Then in your test.js
file:然后在你的
test.js
文件中:
test('useState mock', () => {
const myInitialState = 'My Initial State'
React.useState = jest.fn().mockReturnValue([myInitialState, {}])
const wrapper = shallow(<MyComponent />)
// initial state is set and you can now test your component
}
If you use useState hook multiple times in your component:如果您在组件中多次使用 useState 钩子:
// in MyComponent.js
import React from 'react'
const [myFirstState, setMyFirstState] = React.useState();
const [mySecondState, setMySecondState] = React.useState();
// in MyComponent.test.js
test('useState mock', () => {
const initialStateForFirstUseStateCall = 'My First Initial State'
const initialStateForSecondUseStateCall = 'My Second Initial State'
React.useState = jest.fn()
.mockReturnValueOnce([initialStateForFirstUseStateCall, {}])
.mockReturnValueOnce([initialStateForSecondUseStateCall, {}])
const wrapper = shallow(<MyComponent />)
// initial states are set and you can now test your component
}
// actually testing of many `useEffect` calls sequentially as shown
// above makes your test fragile. I would recommend to use
// `useReducer` instead.
If I recall correctly, you should try to avoid mocking out the built-in hooks like useState
and useEffect
.如果我
useState
useEffect
话,你应该尽量避免模拟像useState
和useEffect
这样的内置钩子。 If it is difficult to trigger the state change using enzyme's invoke()
, then that may be an indicator that your component would benefit from being broken up.如果使用酶的
invoke()
很难触发状态更改,那么这可能表明您的组件将从分解中受益。
const setHookState = (newState) => jest.fn().mockImplementation(() => [ newState, () => {}, ]);
const reactMock = require('react');
In your code, you must use React.useState()
to this work, else it won't work在你的代码中,你必须使用
React.useState()
来完成这项工作,否则它将无法工作
const [arrayValues, setArrayValues] = React.useState();
const [isFetching, setFetching] = React.useState();
reactMock.useState = setHookState({ arrayValues: [], isFetching: false, });
SOLUTION WITH DE-STRUCTURING解构解决方案
You don't need to use React.useState
- you can still destructure in your component.你不需要使用
React.useState
- 你仍然可以在你的组件中解构。
But you need to write your tests in accordance to the order in which your useState calls are made.但是您需要按照进行 useState 调用的顺序编写测试。 For example, if you want to mock two useState calls, make sure they're the first two useState calls in your component.
例如,如果要模拟两个 useState 调用,请确保它们是组件中的前两个 useState 调用。
In your component:在您的组件中:
import React, { useState } from 'react';
const [firstOne, setFirstOne] = useState('');
const [secondOne, setSecondOne] = useState('');
In your test:在您的测试中:
import React from 'react';
jest
.spyOn(React, 'useState')
.mockImplementationOnce(() => [firstInitialState, () => null])
.mockImplementationOnce(() => [secondInitialState, () => null])
.mockImplementation((x) => [x, () => null]); // ensures that the rest are unaffected
//Component
const MyComponent = ({ someColl, someId }) => {
const [myState, setMyState] = useState(null);
useEffect(() => {loop every time group is set
if (groupId) {
const runEffect = async () => {
const data = someColl.find(s => s.id = someId);
setMyState(data);
};
runEffect();
}
}, [someId, someColl]);
return (<div>{myState.name}</div>);
};
// Test
// Mock
const mockSetState = jest.fn();
jest.mock('react', () => ({
...jest.requireActual('react'),
useState: initial => [initial, mockSetState]
}));
const coll = [{id: 1, name:'Test'}, {id: 2, name:'Test2'}];
it('renders correctly with groupId', () => {
const wrapper = shallow(
<MyComponent comeId={1} someColl={coll} />
);
setTimeout(() => {
expect(wrapper).toMatchSnapshot();
expect(mockSetState).toHaveBeenCalledWith({ id: 1, name: 'Test' });
}, 100);
});
I have spent a lot of time but found good solution for testing multiple useState in my app.我花了很多时间,但找到了在我的应用程序中测试多个 useState 的好解决方案。
export const setHookTestState = (newState: any) => {
const setStateMockFn = () => {};
return Object.keys(newState).reduce((acc, val) => {
acc = acc?.mockImplementationOnce(() => [newState[val], setStateMockFn]);
return acc;
}, jest.fn());
};
where newState is object with state fields in my component;其中 newState 是我的组件中具有状态字段的对象;
for example:例如:
React.useState = setHookTestState({
dataFilter: { startDate: '', endDate: '', today: true },
usersStatisticData: [],
});
NOT CHANGING TO React.useState
不更改
React.useState
This approach worked for me:这种方法对我有用:
//import useState with alias just to know is a mock import React, { useState as useStateMock } from 'react' //preseve react as it actually is but useState jest.mock('react', () => ({...jest.requireActual('react'), useState: jest.fn(), })) describe('SearchBar', () => { const realUseState: any = useStateMock //create a ref copy (just for TS so it prevents errors) const setState = jest.fn() //this is optional, you can place jest.fn directly beforeEach(() => { realUseState.mockImplementation((init) => [init, setState]) //important, let u change the value of useState hook }) it('it should execute setGuestPickerFocused with true given that dates are entered', async () => { jest.spyOn(React, 'useState').mockImplementationOnce(() => ['', () => null]) //place the values in the order of your useStates.mockImplementationOnce(() => ['20220821', () => null]) //....mockImplementationOnce(() => ['20220827', () => null]) //... jest.spyOn(uiState, 'setGuestPickerFocused').mockReturnValue('') getRenderedComponent() expect(uiState.setGuestPickerFocused).toHaveBeenCalledWith(true) }) })
My component我的组件
const MyComp: React.FC<MyCompProps> = ({ a, b, c, }) => { const [searchQuery, setSearchQuery] = useState('') // my first value const [startDate, setStartDate] = useState('') // my second value const [endDate, setEndDate] = useState('') // my third value useEffect(() => { console.log(searchQuery, startDate, endDate) // just to verifiy }, [])
Hope this helps希望这可以帮助
I used for multiple useState()
Jest mocks the following setup in the component file我用于多个
useState()
Jest 模拟组件文件中的以下设置
const [isLoading, setLoading] = React.useState(false);
const [isError, setError] = React.useState(false);
Please note the useState
mock will just work with React.useState()
derivation.请注意
useState
模拟仅适用于React.useState()
派生。
..and in the test.js ..在 test.js 中
describe('User interactions at error state changes', () => {
const setStateMock = jest.fn();
beforeEach(() => {
const useStateMock = (useState) => [useState, setStateMock];
React.useState.mockImplementation(useStateMock)
jest.spyOn(React, 'useState')
.mockImplementationOnce(() => [false, () => null]) // this is first useState in the component
.mockImplementationOnce(() => [true, () => null]) // this is second useState in the component
});
it('Verify on list the state error is visible', async () => {
render(<TodoList />);
....
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.