如何在玩笑和酶中为useState Hook设置初始状态?

时间:2019-07-14 08:29:15

标签: javascript reactjs enzyme react-hooks react-hooks-testing-library

目前,Im使用带有React钩子的功能组件。但是我无法完全测试useState Hook。考虑一个场景,在useEffect挂钩中,我正在进行api调用并在useState中设置值。对于玩笑/酶,我有要测试的模拟数据,但无法在玩笑中为useState设置初始状态值。

const [state, setState] = useState([]);

我想将初始状态设置为对象数组。我找不到任何类似于类组件的setState函数。

5 个答案:

答案 0 :(得分:4)

  • 下面的函数将返回状态
const setHookState = (newState) => jest.fn().mockImplementation(() => [
  newState,
  () => {},
]);
  • 在下面添加以使用react

const reactMock = require('react');

在您的代码中,您必须使用React.useState()来完成这项工作,否则它将无法工作

const [arrayValues, setArrayValues] = React.useState();

const [isFetching, setFetching] = React.useState();

  • 然后在您的测试中添加以下模拟状态值

reactMock.useState = setHookState({ arrayValues: [], isFetching: false, });

灵感:Goto

答案 1 :(得分:3)

您可以模拟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

答案 2 :(得分:3)

如果我没记错的话,您应该尽量避免嘲笑诸如useStateuseEffect之类的内置挂钩。如果使用酶的invoke()难以触发状态更改,则表明您的组件将从中受益。

答案 3 :(得分:0)

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

答案 4 :(得分:0)

首先,您不能在组件中使用分解。例如,您不能使用:

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.

如果您还想模拟setState函数:

test('useState mock', () => {
   const mockSetState = jest.fn()       

   const myInitialState = 'My Initial State'
   React.useState = jest.fn().mockReturnValue([myInitialState, mockSetState])

   // if you call setState function in useEffect with `[]` dependency 

   // note that `Enzyme.shallow` does not call `useEffect`
   // so, use `Enzyme.mount` instead
   const wrapper = mount(<MyComponent />)

   // initial state and setState function are mocked and set, 
   // and you can now test your component

   expect(mockSetState.mock.calls.length).toBe(1)

   // to test the argument used when calling setState
   expect(mockSetState.mock.calls[0][0]).toEqual(myArgument)
   // `[0][0]` part is for first argument of first call
   // OR
   expect(mockSetState).toHaveBeenCalledWith(myArgument)
}