目前我正在使用带有反应钩子的功能组件。但我无法useState
完全测试钩子。考虑这样一个场景,在useEffect
钩子中我正在做一个 API 调用并在useState
. 对于 jest/enzyme,我已经模拟了要测试的数据,但我无法useState
在 jest 中设置初始状态值。
const [state, setState] = useState([]);
我想将初始状态设置为开玩笑的对象数组。我找不到任何类似于类组件的 setState 函数。
目前我正在使用带有反应钩子的功能组件。但我无法useState
完全测试钩子。考虑这样一个场景,在useEffect
钩子中我正在做一个 API 调用并在useState
. 对于 jest/enzyme,我已经模拟了要测试的数据,但我无法useState
在 jest 中设置初始状态值。
const [state, setState] = useState([]);
我想将初始状态设置为开玩笑的对象数组。我找不到任何类似于类组件的 setState 函数。
您可以模拟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))
参考: https ://dev.to/theactualgivens/testing-react-hook-state-changes-2oga
首先,您不能在组件中使用解构。例如,您不能使用:
import React, { useState } from 'react';
const [myState, setMyState] = useState();
相反,您必须使用:
import React from 'react'
const [myState, setMyState] = React.useState();
然后在你的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
}
如果您在组件中多次使用 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.
如果我没记错的话,你应该尽量避免模拟内置的钩子,比如useState
and useEffect
。如果使用酶很难触发状态变化invoke()
,那么这可能表明您的组件将从被分解中受益。
//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);
});
const setHookState = (newState) => jest.fn().mockImplementation(() => [ newState, () => {}, ]);
const reactMock = require('react');
在你的代码中,你必须习惯React.useState()
这个工作,否则它不会工作
const [arrayValues, setArrayValues] = React.useState();
const [isFetching, setFetching] = React.useState();
reactMock.useState = setHookState({ arrayValues: [], isFetching: false, });
灵感:后藤
我花了很多时间,但找到了在我的应用程序中测试多个 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());
};
其中 newState 是我的组件中具有状态字段的对象;
例如:
React.useState = setHookTestState({
dataFilter: { startDate: '', endDate: '', today: true },
usersStatisticData: [],
});