Zustand persist中间件在单元测试中未执行的问题求助
Zustand persist中间件测试不生效问题排查
待测试的Zustand状态管理代码
import { create } from "zustand"; import { persist } from "zustand/middleware"; interface LanguageState { language: "en" | "fi"; setLanguage: (lang: "en" | "fi") => void; } const useLanguageStore = create<LanguageState>()( persist( (set) => ({ language: "en", setLanguage: (lang) => set({ language: lang }), }), { name: "language-storage", } ) ); export default useLanguageStore;
测试代码(Jest+TypeScript)
import { renderHook, act, waitFor } from "@testing-library/react"; import useLanguageStore from "../../../src/store/useLanguageStore"; const localStorageMock = (() => { let store: Record<string, string> = {}; return { getItem: (key: string) => store[key] || null, setItem: (key: string, value: string) => { store[key] = value; }, removeItem: (key: string) => { delete store[key]; }, clear: () => { store = {}; }, }; })(); Object.defineProperty(window, "localStorage", { value: localStorageMock, }); describe("useLanguageStore", () => { beforeEach(() => { localStorageMock.clear(); jest.clearAllMocks(); useLanguageStore.setState({ language: "en" }); }); it("1. should have EN as default language and setLanguage when initializing", () => { const { result } = renderHook(() => useLanguageStore()); expect(result.current.language).toBe("en"); expect(result.current.setLanguage).toBeInstanceOf(Function); }); it("2. should change to FI when setLanguage is triggered", () => { const { result } = renderHook(() => useLanguageStore()); act(() => { result.current.setLanguage("fi"); }); expect(result.current.language).toBe("fi"); }); it("3. should change to EN when setLanguage is triggered", () => { const { result } = renderHook(() => useLanguageStore()); act(() => { result.current.setLanguage("en"); }); expect(result.current.language).toBe("en"); }); it("4. should change to FI when setLanguage is triggered multiple times ", () => { const { result } = renderHook(() => useLanguageStore()); act(() => { result.current.setLanguage("en"); }); act(() => { result.current.setLanguage("fi"); }); act(() => { result.current.setLanguage("en"); }); act(() => { result.current.setLanguage("fi"); }); expect(result.current.language).toBe("fi"); }); it(" should persist language to localStorage", async () => { const { result } = renderHook(() => useLanguageStore()); await act(async () => { result.current.setLanguage("fi"); }); await waitFor(() => { const storedLanguage = localStorageMock.getItem("language-storage"); expect(storedLanguage).toBeTruthy(); const parsedLanguage = JSON.parse(storedLanguage!); expect(parsedLanguage.state.language).toBe("fi"); }); }); });
问题描述
最后一个测试用例中,Zustand的persist中间件未在测试环境下执行,导致模拟localStorage中无数据,但该功能在开发和生产环境正常工作。已尝试刷新微任务、Jest假定时器等方案,问题仍未解决。
解决方案
1. 修正beforeEach的状态重置逻辑
直接调用useLanguageStore.setState会绕过persist中间件的状态监听逻辑,改用store提供的setLanguage方法重置状态,确保触发persist的存储逻辑:
beforeEach(() => { localStorageMock.clear(); jest.clearAllMocks(); // 通过setLanguage重置,触发persist逻辑 useLanguageStore.getState().setLanguage("en"); });
或者直接调用persist中间件提供的重置方法,彻底清空存储并重新初始化:
beforeEach(() => { localStorageMock.clear(); jest.clearAllMocks(); // 清空persist存储并重新hydrate useLanguageStore.persist.clearStorage(); useLanguageStore.persist.rehydrate(); });
2. 简化持久化测试的异步处理
Zustand的persist中间件默认是同步写入localStorage的,无需使用await act和waitFor,直接用同步act包裹状态变更即可:
it("should persist language to localStorage", () => { const { result } = renderHook(() => useLanguageStore()); act(() => { result.current.setLanguage("fi"); }); const storedLanguage = localStorageMock.getItem("language-storage"); expect(storedLanguage).toBeTruthy(); const parsedLanguage = JSON.parse(storedLanguage!); expect(parsedLanguage.state.language).toBe("fi"); });
3. 验证localStorage调用(可选)
可以给mock的setItem添加spy,验证persist是否正确调用了localStorage:
// 在beforeEach中添加spy beforeEach(() => { localStorageMock.clear(); jest.clearAllMocks(); jest.spyOn(localStorageMock, "setItem"); useLanguageStore.getState().setLanguage("en"); }); // 在持久化测试中验证 it("should persist language to localStorage", () => { const { result } = renderHook(() => useLanguageStore()); act(() => { result.current.setLanguage("fi"); }); expect(localStorageMock.setItem).toHaveBeenCalledWith( "language-storage", expect.stringContaining('"language":"fi"') ); const storedLanguage = localStorageMock.getItem("language-storage"); const parsedLanguage = JSON.parse(storedLanguage!); expect(parsedLanguage.state.language).toBe("fi"); });
内容的提问来源于stack exchange,提问作者Lollypop123445
相关产品推荐
相关产品推荐

