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

如何测试自定义Hook触发的Zustand状态变更?

解决测试React自定义Hook时Zustand getState返回undefined的问题

核心问题分析

测试useLogIn Hook引发的Zustand状态变化时,getState返回undefined,通常是以下原因导致:

  • Zustand的Mock配置错误,未正确初始化测试环境的store
  • 测试未提供Hook依赖的React Query上下文,导致异步逻辑无法正常执行
  • 未等待React Query的异步操作完成就提前获取状态
  • 组合式store的切片注册不完整

具体解决方案

1. 修复Zustand的Mock配置

避免完全Mock Zustand的核心逻辑,改用保留真实创建逻辑并支持状态重置的Mock,防止测试间状态污染:

// __mocks__/zustand.js
import { act } from '@testing-library/react'
import { create as actualCreate } from 'zustand'

const storeResetFns = new Set()

export const create = (createState) => {
  const store = actualCreate(createState)
  const initialState = store.getState()
  storeResetFns.add(() => store.setState(initialState, true))
  return store
}

export const resetAllStores = () => {
  act(() => {
    storeResetFns.forEach(resetFn => resetFn())
  })
}

2. 测试时正确提供上下文

使用renderHook并包裹React Query的上下文,确保Hook能正常访问依赖:

import { renderHook, waitFor } from '@testing-library/react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { useLogIn } from './useLogIn'
import { useAuthStore } from './store'
import { resetAllStores } from '__mocks__/zustand'

// 初始化React Query客户端
const queryClient = new QueryClient({
  defaultOptions: { queries: { retry: false } }
})

// 上下文包装器
const wrapper = ({ children }) => (
  <QueryClientProvider client={queryClient}>
    {children}
  </QueryClientProvider>
)

// 每次测试前重置状态
beforeEach(() => resetAllStores())

test('useLogIn updates Zustand loading and data state', async () => {
  // 模拟登录API成功响应
  vi.mock('./api', () => ({
    login: vi.fn().mockResolvedValue({ token: 'test-token' })
  }))

  // 渲染Hook
  const { result } = renderHook(() => useLogIn(), { wrapper })

  // 触发登录操作
  result.current.mutate({ username: 'test', password: 'test' })

  // 检查初始加载状态
  const initialState = useAuthStore.getState()
  expect(initialState.isLoading).toBe(true)

  // 等待异步操作完成,验证最终状态
  await waitFor(() => {
    const finalState = useAuthStore.getState()
    expect(finalState.isLoading).toBe(false)
    expect(finalState.userData).toEqual({ token: 'test-token' })
  })
})

3. 验证组合式Store的切片注册

确保Auth切片正确注入根Store,比如:

// store.js
import { create } from 'zustand'
import { authSlice } from './authSlice'

export const useAuthStore = create((...args) => ({
  ...authSlice(...args),
  // 其他切片逻辑
}))

authSlice需返回包含isLoading、userData及对应更新方法的对象:

// authSlice.js
export const authSlice = (set) => ({
  isLoading: false,
  userData: null,
  setIsLoading: (loading) => set({ isLoading: loading }),
  setUserData: (data) => set({ userData: data })
})

4. 检查自定义Hook的状态更新逻辑

确保useLogIn正确调用Zustand的状态更新方法:

// useLogIn.js
import { useMutation } from '@tanstack/react-query'
import { useAuthStore } from './store'
import { login } from './api'

export const useLogIn = () => {
  const setIsLoading = useAuthStore(state => state.setIsLoading)
  const setUserData = useAuthStore(state => state.setUserData)

  return useMutation({
    mutationFn: credentials => login(credentials),
    onMutate: () => setIsLoading(true),
    onSuccess: data => {
      setUserData(data)
      setIsLoading(false)
    },
    onError: () => setIsLoading(false)
  })
}

常见坑点

  • 不要直接Mock整个Zustand store,保留真实的create逻辑才能正确获取状态
  • 若使用Zustand的persist中间件,测试时需Mock本地存储或禁用持久化
  • 必须等待React Query的异步操作完成(用waitFor)后再检查状态

内容的提问来源于stack exchange,提问作者Sahitha Nelanga

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 11:32:21