开发者问题收集

使用 Jest、RTL、Redux Toolkit、React 进行测试时模拟 useAppSelector 的问题

2023-12-03
219

我已安装 react redux 工具包,并添加了 jest 而不是 vitest 进行测试。

我想测试模式窗口是否在 App 组件中呈现(如果 isOpen 标志为 true)。我只模拟了我需要的商店部分。不是整个商店。

应用组件

function App() {
  const { isOpen: isPostModalWindowOpen } = useAppSelector(selectPostModal)

  return (
    <div className="app-container" id="app-container">
      {isPostModalWindowOpen && <PostModalWindow />}
      <Posts />
      <PageLoader />
      <Alert />
    </div>
  )
}

export default App

测试文件

import App from "../App"
import { PostModalWindow } from "../components/PostModalWindow/PostModalWindow"
import { selectPostModal } from "../redux/postModal"
import { Alert } from "../components/Alert/Alert"
import { PageLoader } from "../components/PageLoader/PageLoader"
import { Posts } from "../components/Posts/Posts"

import { useAppSelector } from "../redux/hooks"
import { render } from "@testing-library/react"
import { RootState } from "../redux/store"
import { clone } from "ramda"

jest.mock("../App", () => ({
  ...jest.requireActual("../App"),
  App: jest.fn(),
}))
jest.mock("../components/PostModalWindow/PostModalWindow", () => ({
  ...jest.requireActual("../components/PostModalWindow/PostModalWindow"),
  PostModalWindow: jest.fn(),
}))
jest.mock("../redux/hooks", () => ({
  ...jest.requireActual("../redux/hooks"),
  useAppSelector: jest.fn(),
}))
jest.mock("../redux/postModal", () => ({
  ...jest.requireActual("../redux/postModal"),
  selectPostModal: jest.fn(),
}))
jest.mock("../components/Alert/Alert", () => ({
  ...jest.requireActual("../components/Alert/Alert"),
  Alert: jest.fn(),
}))
jest.mock("../components/PageLoader/PageLoader", () => ({
  ...jest.requireActual("../components/PageLoader/PageLoader"),
  PageLoader: jest.fn(),
}))
jest.mock("../components/Posts/Posts", () => ({
  ...jest.requireActual("../components/Posts/Posts"),
  Posts: jest.fn(),
}))

describe("Test <App />", () => {
  beforeEach(() => {
    jest.clearAllMocks()

    const initialState = {
      postModal: {
        isOpen: true,
      },
    } as RootState
    let currentState: RootState

    currentState = clone(initialState)

    jest.mocked(useAppSelector).mockImplementation((fn) => fn(currentState))
  })

  it("show modal window if isOpen is true", () => {
    const { container } = render(<App />)
    expect(container).toMatchSnapshot()
  })
})

如何修复此测试错误: TypeError:无法解构'(0 , hooks_1.useAppSelector)(...)'的属性'isOpen',因为它未定义。

我在 jest.mocked(useAppSelector).mockImplementation((fn) => fn(currentState)) 中console.logged当前状态,它返回 postModal: {isOpen: true> ,所以我不知道为什么它说它未定义。

1个回答

我通过意识到测试方法中的两个错误解决了这个问题。

首先,我错误地尝试模拟 App 组件。由于我们正在测试 App 组件本身,因此无需模拟它。模拟它会干扰实际的测试过程。

其次,我错误地处理了 selectPostModal 的模拟。我没有提供值,而是尝试创建一个模拟。

import App from "../App"
import { PostModalWindow } from "../components/PostModalWindow/PostModalWindow"
import { useAppSelector } from "../redux/hooks"
import { Alert } from "../components/Alert/Alert"
import { PageLoader } from "../components/PageLoader/PageLoader"
import { Posts } from "../components/Posts/Posts"

import { render } from "@testing-library/react"
import { RootState } from "../redux/store"
import { clone } from "ramda"

jest.mock("../components/PostModalWindow/PostModalWindow", () => ({
  ...jest.requireActual("../components/PostModalWindow/PostModalWindow"),
  PostModalWindow: jest.fn(),
}))
jest.mock("../redux/hooks", () => ({
  ...jest.requireActual("../redux/hooks"),
  useAppSelector: jest.fn(),
}))
jest.mock("../components/Alert/Alert", () => ({
  ...jest.requireActual("../components/Alert/Alert"),
  Alert: jest.fn(),
}))
jest.mock("../components/PageLoader/PageLoader", () => ({
  ...jest.requireActual("../components/PageLoader/PageLoader"),
  PageLoader: jest.fn(),
}))
jest.mock("../components/Posts/Posts", () => ({
  ...jest.requireActual("../components/Posts/Posts"),
  Posts: jest.fn(),
}))

describe("Test <App />", () => {
  const initialState = {
    postModal: {
      isOpen: true,
    },
  } as RootState
  let currentState: RootState

  beforeEach(() => {
    jest.clearAllMocks()

    currentState = clone(initialState)

    jest.mocked(useAppSelector).mockImplementation((fn) => fn(currentState))
    jest
      .mocked(PostModalWindow)
      .mockImplementation(() => <div>mockedPostModalWindow</div>)
    jest.mocked(Posts).mockImplementation(() => <div>mockedPosts</div>)
    jest
      .mocked(PageLoader)
      .mockImplementation(() => <div>mockedPageLoader</div>)
    jest.mocked(Alert).mockImplementation(() => <div>mockedAlert</div>)
  })

  it("show modal window if isOpen is true", () => {
    const { container } = render(<App />)
    expect(container).toMatchSnapshot()
  })

  it("Don't show modal window if isOpen is false", () => {
    currentState.postModal.isOpen = false

    const { container } = render(<App />)
    expect(container).toMatchSnapshot()
  })
})
George Tsintsadze
2023-12-04