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

React组件Jest测试:Mock Store、函数及设置初始State方法

React组件Jest测试Mock问题

组件代码(ParentComponent.js)

import React, {useEffect, useState} from 'react'
import store from '../store_path'
import loadable from '@loadable/component'
import useTokenHook from 'hook_path'

const ChildComponent = loadable(() => import('../childComponent_path'))

export const ParentComponent = () => {
    const token = useTokenHook()
    const [data, setData] = useState({})
    const [showChild, setShowChild] = useState(false)
    const lang = store(state => state.lang)
    const apiCall = store(state => state.apicall)

    const myFn = async() => {
        const res = await apiCall();
        setData(res)
        setShowChild(true)
    }

    useEffect(() => {
        myFn()
    }, [])

    return (
        <>
            {showChild
              ? <ChildComponent data={data} />
              : 'No data found'
            }
        </>
    )
}

需求

为该组件编写Jest测试用例,需要实现:

  • Mock store,设置lang默认值为'en'
  • 让apiCall返回指定值
  • 在测试中将showChild的初始值设为true

尝试过的方法及报错

方法1

jest.mock('../store_path', () => ({
    lang: 'en',
    apiCall: jest.fn(() => {
        return someValue
     })
}))

报错:

TypeError: (0, _store.default) is not a function

方法2

const appStore = jest.mock('../store_path', () => jest.fn())

appStore.mockImplementation(() => ({
  lang: 'en',
  apiCall: jest.fn(() => {
    return someValue
  })
}))

报错:

appStore.mockImplementation is not a function

解决方案

1. 正确Mock Store

你代码里的store是个函数(因为用了store(state => state.lang)这种调用方式),所以mock时不能直接返回对象,得返回一个能接收选择器参数的函数,根据选择器返回对应值:

// 定义要mock的store状态
const mockStoreState = {
  lang: 'en',
  apicall: jest.fn().mockResolvedValue('你要指定的返回值') // 设置apiCall的返回值
}

// Mock store模块
jest.mock('../store_path', () => {
  // 返回模拟的store函数,传入选择器后返回对应状态值
  return jest.fn((selector) => selector(mockStoreState))
})

这样组件调用store(state => state.lang)时会拿到'en',调用store(state => state.apicall)时会拿到预先mock好的函数。

2. 将showChild初始值设为true

给你两种简单实现方式:

方式一:直接Mock useState指定初始值

通过拦截useState调用,修改对应状态的初始值:

import React from 'react'
import { render } from '@testing-library/react'
import { ParentComponent } from './ParentComponent'

// 拦截useState,判断初始值为false时返回true
jest.spyOn(React, 'useState').mockImplementation((initialValue) => {
  if (initialValue === false) {
    return [true, jest.fn()] // 第一个值是状态,第二个是setter(用jest.fn()占位即可)
  }
  // 其他useState调用保留原初始值
  return [initialValue, jest.fn()]
})

test('showChild初始为true时渲染子组件', () => {
  const { getByTestId } = render(<ParentComponent />)
  // 假设子组件添加了data-testid="child-component",验证它存在
  expect(getByTestId('child-component')).toBeInTheDocument()
})

方式二:等待组件自然触发状态变化

如果不需要强制初始值,只是验证api调用后showChild变为true的状态,直接等待useEffect执行完成即可:

import { render, waitFor } from '@testing-library/react'
import { ParentComponent } from './ParentComponent'

test('api调用完成后渲染子组件', async () => {
  const { getByText, getByTestId } = render(<ParentComponent />)
  
  // 初始状态显示"No data found"
  expect(getByText('No data found')).toBeInTheDocument()
  
  // 等待api调用完成,状态更新
  await waitFor(() => {
    expect(getByTestId('child-component')).toBeInTheDocument()
  })
})

完整测试示例

import React from 'react'
import { render, waitFor } from '@testing-library/react'
import { ParentComponent } from './ParentComponent'

// Mock store
const mockApiCall = jest.fn().mockResolvedValue({ name: '测试数据' })
jest.mock('../store_path', () => jest.fn((selector) => {
  return selector({
    lang: 'en',
    apicall: mockApiCall
  })
}))

// Mock异步加载的子组件(避免测试中的异步问题)
jest.mock('@loadable/component', () => (loadFn) => {
  const Component = loadFn().default
  return Component
})
// 或者直接Mock子组件
jest.mock('../childComponent_path', () => () => <div data-testid="child-component" />)

test('组件正确渲染并处理api返回', async () => {
  // Mock useState让showChild初始为true
  jest.spyOn(React, 'useState').mockImplementation((init) => {
    if (init === false) return [true, jest.fn()]
    return [init, jest.fn()]
  })

  const { getByTestId } = render(<ParentComponent />)
  
  // 验证子组件渲染
  expect(getByTestId('child-component')).toBeInTheDocument()
  
  // 验证apiCall被调用
  expect(mockApiCall).toHaveBeenCalled()
})

内容的提问来源于stack exchange,提问作者Urvesh Nagda

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 12:25:22