如何使用Zustand和Jest测试异步状态更新?
Zustand Store异步方法测试问题解决
问题场景
我有如下listStore.js文件:
import { create } from 'zustand' import queryList from '@/service' const useListStore = () => create((get, set) => ({ list: [], getList: async () => { const list = await queryList() const { updateList } = get() updateList(list) }, updateList: list => { set({ list: list.map(i => i + 1) }) } }))
为测试该Store Hook,编写了listStore.test.js测试文件:
import useListStore from './useListStore' import { renderHook, act } from '@testing-library/react' jest.mock('@/service', () => ({ queryList: jest.fn().mockResolvedValue([1,2,3]) })) describe('test listStore', () => { test('test updateList function', () => { // some codes }) test('test getList function', () => { const { result } = renderHook(() => useListStore()) act(() => result.current.getList()) expect(queryList).toHaveBeenCalled() expect(result.current.list).toEqual([2,3,4]) }) })
测试getList函数时,无法确保状态更新完成,即便用waitFor或await,拿到的list仍为旧值[],而非期望的[2,3,4]。
问题根源与解决步骤
1. 修复Store定义错误
你的useListStore写法有误:当前是每次调用useListStore()都会创建一个全新的Store实例,这不符合Zustand的使用规范,会导致状态无法在组件或测试中共享更新。
正确的Store定义:
import { create } from 'zustand' import queryList from '@/service' // 直接将create的返回值赋值给useListStore,无需包裹成函数 const useListStore = create((get, set) => ({ list: [], getList: async () => { const list = await queryList() const { updateList } = get() updateList(list) }, updateList: list => { set({ list: list.map(i => i + 1) }) } })) export default useListStore
2. 修正测试代码的异步处理
getList是异步函数,测试时必须等待其执行完成,同时Zustand的状态更新是异步的,需要用waitFor确保状态同步到Hook返回值后再断言。
修改后的测试代码:
import useListStore from './useListStore' import { renderHook, act, waitFor } from '@testing-library/react' import queryList from '@/service' jest.mock('@/service', () => ({ queryList: jest.fn().mockResolvedValue([1,2,3]) })) describe('test listStore', () => { test('test updateList function', () => { // 你的测试代码 }) test('test getList function', async () => { const { result } = renderHook(() => useListStore()) // 用async/await等待异步的getList执行完成 await act(async () => { await result.current.getList() }) // 验证queryList被调用 expect(queryList).toHaveBeenCalled() // 用waitFor等待状态更新完成,再断言list的值 await waitFor(() => { expect(result.current.list).toEqual([2,3,4]) }) }) })
关键说明
act包裹异步操作时必须使用async/await,确保异步函数执行完毕后再进行后续断言。waitFor会持续检查断言条件,直到状态更新完成(默认超时时间1000ms),能有效解决Zustand状态更新与测试断言不同步的问题。
内容的提问来源于stack exchange,提问作者Chor
相关产品推荐
相关产品推荐

