模拟react-redux的useSelector和jest,用react测试库进行测试

Mar*_*tes 3 unit-testing reactjs jestjs react-redux react-testing-library

我正在尝试测试一个使用react-redux 进行状态管理的组件。

为了快速测试,我想模拟 useSelector,如下所示:

const templates = [
  {
    id: ...,
    name: ...,
    ...
  },
  {
    id: ...,
    name: ...,
    ...
  },
]


jest.mock('react-redux', () => ({
  ...jest.requireActual('react-redux'),
  useSelector: jest.fn(),
}));


describe('some awesome description', () => {
  beforeEach(() => {
    useSelector.mockImplementation(callback => callback(templates));
  });
});
Run Code Online (Sandbox Code Playgroud)

但在运行测试时,会失败,如下所示:

TypeError: Cannot read properties of undefined (reading 'ids')

  141 | describe('layouts/TemplatesPanel', () => {
  142 |   beforeEach(() => {
> 143 |     useSelector.mockImplementation(callback => callback(templates));
      |                                                ^
Run Code Online (Sandbox Code Playgroud)

sli*_*wp2 7

最好不要使用mock useSelector,mock的实现可能会破坏它的功能,因为它的功能不仅仅是返回某个状态片。查看useSelector 真正的实现,它不只是返回selectedState.

\n

推荐的方法是创建一个模拟商店并为其提供模拟数据。

\n

例如

\n

index.tsx

\n
import React from \'react\';\nimport { useSelector } from \'react-redux\';\n\nexport type Template = {\n  id: string;\n  name: string;\n};\nexport type RootState = {\n  templates: Template[];\n};\n\nexport const MyComp = () => {\n  const templates = useSelector<RootState>((state) => state.templates);\n  console.log(\'templates: \', templates);\n  return <div>MyComp</div>;\n};\n
Run Code Online (Sandbox Code Playgroud)\n

index.test.tsx

\n
import { render } from \'@testing-library/react\';\nimport React from \'react\';\nimport { Provider } from \'react-redux\';\nimport { createStore } from \'redux\';\nimport { MyComp, RootState, Template } from \'.\';\n\ndescribe(\'73494842\', () => {\n  test(\'should pass\', () => {\n    const templates: Template[] = [\n      { id: \'1\', name: \'a\' },\n      { id: \'2\', name: \'b\' },\n    ];\n    const mockStore = createStore<RootState, any, any, any>((state = { templates }, action) => {\n      if (action.type === \'UPATE_NAME\') {\n        return {\n          ...state,\n          templates: templates.map((t) => (t.id === action.payload.id ? { ...t, name: action.payload.name } : t)),\n        };\n      }\n      return state;\n    });\n    render(\n      <Provider store={mockStore}>\n        <MyComp />\n      </Provider>\n    );\n    mockStore.dispatch({ type: \'UPATE_NAME\', payload: { id: \'1\', name: \'c\' } });\n  });\n});\n
Run Code Online (Sandbox Code Playgroud)\n

测试结果:

\n
import React from \'react\';\nimport { useSelector } from \'react-redux\';\n\nexport type Template = {\n  id: string;\n  name: string;\n};\nexport type RootState = {\n  templates: Template[];\n};\n\nexport const MyComp = () => {\n  const templates = useSelector<RootState>((state) => state.templates);\n  console.log(\'templates: \', templates);\n  return <div>MyComp</div>;\n};\n
Run Code Online (Sandbox Code Playgroud)\n

当我们稍后发送一个动作时,useSelector钩子将订阅存储的更改,它将再次执行并获取更新的状态切片。如果你模拟它只是返回一个状态切片,那么这个功能就不再起作用了。

\n