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

React+Zustand测试环境localStorage未定义问题求助

Zustand useAuthStore 无法识别全局模拟的 localStorage,抛出 ReferenceError: localStorage is not defined

我正在为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 还未被模拟,直接触发未定义错误。

解决步骤

  1. 提前在测试全局配置中初始化 localStorage 模拟
    将 LocalStorageMock 的挂载移到测试环境的全局设置文件(比如 setupTests.ts)中,确保模块加载前全局就存在 localStorage 对象:

    import { LocalStorageMock } from './utils/globalMocks/localStorage.mock';
    
    global.localStorage = new LocalStorageMock();
    
  2. 延迟读取 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。

  3. 移除冲突依赖
    测试代码中同时引入了 mock-local-storage 和自定义 LocalStorageMock,二者可能冲突。选择其中一种模拟方式,比如删除 import 'mock-local-storage';,仅使用自定义的 LocalStorageMock。

内容的提问来源于stack exchange,提问作者Igor Brizack

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 13:07:07