如何设置初始状态 使用状态 钩子 在玩笑和酶



目前我使用带有反应钩子的功能组件。但是我无法完全测试useState钩子。考虑一个场景,例如,useEffect钩子中,我正在执行 API 调用并在useState中设置值。对于开玩笑/酶,我已经模拟了要测试的数据,但我无法在开玩笑中为useState设置初始状态值。

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.
如果我

没记错的话,你应该尽量避免嘲笑像useStateuseEffect这样的内置钩子。如果很难使用酶的invoke()触发状态变化,那么这可能表明您的组件将从分解中受益。

解构的解决方案

您不需要使用 React.useState - 您仍然可以在组件中解构。

但是,您需要根据进行 useState 调用的顺序编写测试。例如,如果要模拟两个 useState 调用,请确保它们是组件中的前两个 useState 调用。

在组件中:

import React, { useState } from 'react';
const [firstOne, setFirstOne] = useState('');
const [secondOne, setSecondOne] = useState('');

在您的测试中:

import React from 'react';
jest
.spyOn(React, 'useState')
.mockImplementationOnce(() => [firstInitialState, () => null])
.mockImplementationOnce(() => [secondInitialState, () => null])
.mockImplementation((x) => [x, () => null]); // ensures that the rest are unaffected
  • 下面的函数将返回状态
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,
});

灵感:转到

//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);
});

我花了很多时间,但找到了在我的应用程序中测试多个 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: [],
    });

我用于多个useState() 开玩笑在组件文件中模拟以下设置

const [isLoading, setLoading] = React.useState(false);
const [isError, setError] = React.useState(false);

请注意,useState模拟将只适用于React.useState()派生。

..并在测试中.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 />);
    ....

这里有如何在没有酶的情况下轻松做到这一点。如果你使用上下文,你甚至可以做到这一点。

我的组件.js

const [comments, setComments] = useState();

MyComponent.test.js

const comments = [{id:1, title: "first comment", body: "bla bla"}]
jest.spyOn(React, 'useState').mockReturnValueOnce([comments, jest.fn()]);
const { debug } = render(<MyComponent />);
debug();

最后两行代码是看 DOM 的样子,看看你的注释状态在呈现时是否是什么样子的。

不会更改为

React.useState

这种方法对我有用:

//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)
  })
})

我的组件

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
  }, [])

希望这有帮助!

相关内容

  • 没有找到相关文章