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

同一测试中切换API响应测试组件状态的问题(useSWR+Jest)

问题描述

我要测试一个组件,它先显示加载界面,等API返回数据后再展示对应内容。我想在同一个测试用例里完成状态切换测试(不想拆分到不同测试套件),因为API可能随时间返回更多数据。我用了useSWR,但测试时Mock了自定义hook useCustomHook,步骤如下:

  1. 导入并Mock该hook
  2. 渲染组件,断言加载状态显示
  3. 修改Mock hook的响应内容
  4. 但第二次断言不通过,而且Mock hook没被再次调用

测试代码如下:

import { useCustomHook } from "./useCustomHook"

jest.mock(
  "useCustomHook",
  () => ({
    useCustomHook: jest.fn(() => ({
      isLoading: true,
      response: undefined,
    })),
  }),
)
const mockuseCustomHook = useCustomHook as jest.Mock

describe("Component", () => {
  it('Should render properly', () => {
    render(<Component />)

    expect(screen.getByText('Loading')).toBeInTheDocument()

    mockuseCustomHook.mockImplementationOnce(() => ({
      isLoading: false,
      response: { data: 'This is the data' },
    }))

    expect(screen.queryByText('Loading')).not.toBeInTheDocument()
    expect(screen.getByText('This is the data')).toBeInTheDocument()
  })
})
问题原因

核心问题是修改Mock实现后,组件没有触发重新渲染。React组件只会在props变化、内部state更新或依赖项变更时重新渲染,你只是修改了Mock hook的返回逻辑,但组件已经完成初始渲染,hook不会自动重新执行,自然不会更新界面状态。

解决方案

方案1:用rerender触发组件更新(推荐)

React Testing Library的render函数会返回rerender方法,可高效触发组件重新渲染,复用已有的DOM结构:

describe("Component", () => {
  it('Should render properly', () => {
    // 保存rerender方法
    const { rerender } = render(<Component />)

    // 验证初始加载状态
    expect(screen.getByText('Loading')).toBeInTheDocument()

    // 更新Mock的持久化实现(不要用mockImplementationOnce,避免单次调用限制)
    mockuseCustomHook.mockImplementation(() => ({
      isLoading: false,
      response: { data: 'This is the data' },
    }))

    // 触发组件重新渲染,读取新的Mock返回值
    rerender(<Component />)

    // 验证数据展示状态
    expect(screen.queryByText('Loading')).not.toBeInTheDocument()
    expect(screen.getByText('This is the data')).toBeInTheDocument()
  })
})

方案2:重新调用render

如果不需要复用DOM结构,也可以直接再次调用render重新渲染组件:

describe("Component", () => {
  it('Should render properly', () => {
    // 初始渲染,验证加载状态
    render(<Component />)
    expect(screen.getByText('Loading')).toBeInTheDocument()

    // 更新Mock实现
    mockuseCustomHook.mockImplementation(() => ({
      isLoading: false,
      response: { data: 'This is the data' },
    }))

    // 重新渲染组件
    render(<Component />)

    // 验证数据展示状态
    expect(screen.queryByText('Loading')).not.toBeInTheDocument()
    expect(screen.getByText('This is the data')).toBeInTheDocument()
  })
})
注意事项
  • 避免使用mockImplementationOnce:初始渲染已经消耗了一次Mock调用,后续重新渲染需要持久化的Mock逻辑,改用mockImplementation即可。
  • 如果useCustomHook接收参数,可在mockImplementation中添加条件判断,匹配不同参数返回对应结果。

内容的提问来源于stack exchange,提问作者Angelo Dias

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 18:09:18