React+Zustand测试环境localStorage未定义问题求助
我正在为React应用搭建测试环境,需要模拟认证、localStorage等功能。已经创建了LocalStorageMock来存储JSON Web Token,但Zustand的useAuthStore始终无法识别这个模拟对象,持续抛出「ReferenceError: localStorage is not defined」错误。测试前已在beforeEach中初始化LocalStorageMock,尝试过查阅资料、官方文档及AI工具仍未解决,预期全局模拟localStorage后可正常工作,但Zustand相关逻辑存在未知问题。
1. useAuthStore 代码
export const useAuthStore = create<AuthenticationsStore>((set) => ({ authentication: { token: localStorage.getItem('token') || ('' as string), permissions: [], originalGroupId: 0, }, setAuthentication: (authentication) => set(() => ({ authentication: authentication, })), setPermissions: (permissions: string[]) => set((state) => ({ authentication: { ...state.authentication, permissions: permissions, }, })), setToken: (token: string) => set((state) => { localStorage.setItem('token', token); return { authentication: { ...state.authentication, token: token, }, }; }), setOriginalGroupId: (originalGroupId: number) => set((state) => ({ authentication: { ...state.authentication, originalGroupId: originalGroupId, }, })), dismissAuthentication: () => set(() => { localStorage.removeItem('token'); return { authentication: { token: '', permissions: [], originalGroupId: 0, }, }; }), }));
2. LocalStorageMock 代码
export class LocalStorageMock implements Storage { private store: Record<string, string>; constructor() { this.store = {}; } getItem(key: string): string | null { return this.store[key] || null; } setItem(key: string, value: string): void { this.store[key] = value.toString(); } removeItem(key: string): void { delete this.store[key]; } clear(): void { this.store = {}; } // 实现 Storage 接口的属性 get length(): number { return Object.keys(this.store).length; } key(index: number): string | null { const keys = Object.keys(this.store); return index >= 0 && index < keys.length ? keys[index] : null; } } global.localStorage = new LocalStorageMock();
3. 测试实现代码
import { render, fireEvent, screen } from '@testing-library/react'; import PaginaInicial from '../../../features/inicial/routes/PaginaInicial'; import { accessToken } from '../../utils/globalMocks/access-token-json.mock.'; import { LocalStorageMock } from '../../utils/globalMocks/localStorage.mock'; import 'mock-local-storage'; describe('Página Inicial', () => { beforeEach(() => { if (!global.localStorage) { global.localStorage = new LocalStorageMock(); } global.localStorage.setItem( 'token', accessToken.token_type + ' ' + accessToken.access_token, ); }); afterEach(() => { global.localStorage.clear(); }); test('Renderização componentes página inicial', async () => { render(<PaginaInicial />); // 测试剩余代码 }); });
4. 错误信息

问题原因及解决方案
核心原因
Zustand store 在模块加载阶段就会执行初始化逻辑(create 函数内的代码),此时测试代码的 beforeEach 尚未执行,全局 localStorage 还未被模拟,直接触发未定义错误。
解决步骤
提前在测试全局配置中初始化 localStorage 模拟
将 LocalStorageMock 的挂载移到测试环境的全局设置文件(比如setupTests.ts)中,确保模块加载前全局就存在 localStorage 对象:import { LocalStorageMock } from './utils/globalMocks/localStorage.mock'; global.localStorage = new LocalStorageMock();延迟读取 localStorage 到组件使用阶段
修改 store,避免在初始化时直接访问 localStorage,改用初始化方法延迟执行:export const useAuthStore = create<AuthenticationsStore>((set) => ({ authentication: { token: '', permissions: [], originalGroupId: 0, }, initAuth: () => { const token = localStorage.getItem('token') || ''; set(state => ({ authentication: { ...state.authentication, token } })); }, // 其余方法保持不变 setAuthentication: (authentication) => set(() => ({ authentication: authentication, })), // ... 剩余方法 }));在根组件或测试中的组件调用
initAuth,确保 localStorage 模拟完成后再读取 token。移除冲突依赖
测试代码中同时引入了mock-local-storage和自定义 LocalStorageMock,二者可能冲突。选择其中一种模拟方式,比如删除import 'mock-local-storage';,仅使用自定义的 LocalStorageMock。
内容的提问来源于stack exchange,提问作者Igor Brizack

