You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Testing Library+Jest测试组件报错Cannot read property 'off' of undefined如何解决

报错原因

你遇到的TypeError: Cannot read property 'off' of undefined报错,根源是测试运行时组件依赖的外部上下文没有被注入:
组件内部通过useContext(Local.ContextCalculator)获取上下文对象,但测试代码直接渲染<Buttons />,外层没有对应Context.Provider提供值,导致useContext返回undefined,解构得到的setFlag自然也是undefined,访问setFlag.off就触发了报错。
另外组件还依赖useTranslation国际化钩子,测试环境如果没有配置i18n,也会出现翻译值异常。

解决方案

确实需要对依赖做mock或者在测试环境注入依赖,以下是两种常用实现方式:

方式1:Mock依赖(轻量快速)

直接通过jest mock钩子的返回值,不需要额外引入上下文Provider:

// 测试文件顶部添加mock逻辑
jest.mock('react', () => ({
  ...jest.requireActual('react'),
  useContext: jest.fn()
}))

jest.mock('react-i18next', () => ({
  useTranslation: () => ({
    // 直接返回翻译key,避免依赖i18n配置
    t: (key) => key
  })
}))

describe('Buttons', () => {
  const {Buttons} = jest.requireActual(
    '../../../src/components/calculator/Buttons'
  )

  afterEach(() => {
    Lib.resetAll()
    jest.clearAllMocks()
  })

  it('renders as expect', async () => {
    // 模拟上下文返回值
    let mockFlag = false
    const mockSetFlag = {
      on: jest.fn(() => { mockFlag = true }),
      off: jest.fn(() => { mockFlag = false })
    }
    require('react').useContext.mockReturnValue({
      flag: mockFlag,
      setFlag: mockSetFlag
    })

    const view = Lib.render(<Buttons />)
    expect(view.asFragment()).toMatchSnapshot()

    Lib.fireEvent.click(Lib.screen.getByText(/buttons\.calculate/i))
    expect(mockSetFlag.on).toHaveBeenCalled()
    await Lib.screen.findByTestId('result-table-calculator')

    Lib.fireEvent.click(Lib.screen.getByText(/buttons\.clear/i))
    expect(mockSetFlag.off).toHaveBeenCalled()
    expect(
      Lib.screen.queryByTestId('result-table-calculator')
    ).not.toBeInTheDocument()
  })
})

方式2:外层包裹Provider(更贴近真实场景)

如果需要更贴近生产运行逻辑,可以在渲染时直接套上上下文Provider,不需要mock useContext:

// 仅需mock国际化钩子,上下文直接用真实Provider注入
jest.mock('react-i18next', () => ({
  useTranslation: () => ({
    t: (key) => key
  })
}))

describe('Buttons', () => {
  const {Buttons} = jest.requireActual(
    '../../../src/components/calculator/Buttons'
  )
  // 替换为你项目中上下文定义的实际路径
  const {Local} = require('../../../src/xxx/context-defination-path')

  afterEach(() => {
    Lib.resetAll()
  })

  it('renders as expect', async () => {
    // 写个测试用的包装组件管理上下文状态
    const TestWrapper = ({children}) => {
      const [flag, setFlagBase] = React.useState(false)
      const setFlag = {
        on: () => setFlagBase(true),
        off: () => setFlagBase(false)
      }
      return (
        <Local.ContextCalculator.Provider value={{flag, setFlag}}>
          {children}
        </Local.ContextCalculator.Provider>
      )
    }

    const view = Lib.render(<Buttons />, {wrapper: TestWrapper})
    expect(view.asFragment()).toMatchSnapshot()

    Lib.fireEvent.click(Lib.screen.getByText(/buttons\.calculate/i))
    await Lib.screen.findByTestId('result-table-calculator')

    Lib.fireEvent.click(Lib.screen.getByText(/buttons\.clear/i))
    expect(
      Lib.screen.queryByTestId('result-table-calculator')
    ).not.toBeInTheDocument()
  })
})

内容的提问来源于stack exchange,提问作者Renan Bessa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 04:54:04