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

Angular测试报错:无法在初始化前访问UpdatesModalStore

Angular测试报错:Cannot access 'UpdatesModalStore' before initialization 原因分析

问题背景

运行Angular测试时触发异常:

Error during loading: Uncaught ReferenceError: Cannot access 'UpdatesModalStore' before initialization

已通过npx madge --circular --extensions ts ./确认代码无循环依赖,注释掉localStorage的读写操作后测试可正常运行。

服务代码

import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable, take } from 'rxjs';
import { MatSnackBar } from '@angular/material/snack-bar';
import { UpdateNotificationStorageEnum } from '../components/updates-modal/updates-modal.component';
import { UpdateTile } from '../components/updates-modal/update-tile/update-tile.component';

@Injectable({
  providedIn: 'root'
})
export class UpdatesModalStore {
  private updateNotificationCountBS$ = new BehaviorSubject<number>(0);
  private updatesOpenBS$ = new BehaviorSubject<boolean>(false);
  private updatesTilesBS$ = new BehaviorSubject<UpdateTile[]>([]);

  updateNotificationCount$ = this.updateNotificationCountBS$.asObservable();
  updatesOpen$ = this.updatesOpenBS$.asObservable();
  updatesTiles$ = this.updatesTilesBS$.asObservable();

  constructor(
    private snackbar: MatSnackBar
  ) { }

  setUpdateNotificationCount(count: number): void {
    this.updateNotificationCountBS$.next(count);
  }
  setUpdatesOpen(open: boolean): void {
    this.updatesOpenBS$.next(open);
  }

  initUpdatesModal(): void {
    const notificationCount = localStorage.getItem(UpdateNotificationStorageEnum.UPDATE_NOTIFICATION_COUNT);
    if (!notificationCount) {
      this.setUpdateNotificationCount(1);
      localStorage.setItem(UpdateNotificationStorageEnum.UPDATE_NOTIFICATION_COUNT, '1');
    } else {
      this.setUpdateNotificationCount(+notificationCount);
    }
  }

  toggleModal(): void {
    this.updatesOpen$.pipe(take(1)).subscribe((open: boolean) => {
      const newOpenState = !open;
      this.setUpdatesOpen(newOpenState);
    });
  }

  setCurrentStep(steps: UpdateTile[]): void {
    this.updatesTilesBS$.next(steps);
  }

  setSmartSmartNotesEnabled(): void {
    localStorage.setItem(UpdateNotificationStorageEnum.UPDATE_NOTIFICATION_STEP, '2');
    localStorage.setItem(UpdateNotificationStorageEnum.UPDATE_NOTIFICATION_COUNT, '2');
    this.setUpdateNotificationCount(2);
  }
}

报错原因

  • 测试环境localStorage未正确模拟:Angular测试默认使用Jasmine/Karma,测试启动阶段服务初始化时,localStorage可能还未被测试框架正确初始化或模拟。直接调用localStorage的读写方法会触发依赖链的提前执行,导致UpdatesModalStore在完成实例化前被引用。

  • 服务初始化时机冲突:如果服务通过APP_INITIALIZER在应用启动时调用了initUpdatesModal,测试环境下这个执行时机可能早于服务完全初始化的节点。此时执行localStorage操作会间接触发组件或其他依赖对服务的引用,引发初始化顺序错误。

  • 隐性依赖的加载顺序问题:虽然madge未检测到循环依赖,但UpdateNotificationStorageEnum所在的组件可能在模板或逻辑中注入了UpdatesModalStore,测试时模块加载顺序与生产环境不同,导致变量提升阶段出现服务未初始化就被引用的情况。

解决建议

  • 测试中模拟localStorage:在测试文件的beforeEach中添加localStorage的模拟,避免直接操作真实存储:
beforeEach(() => {
  spyOn(localStorage, 'getItem').and.returnValue('1');
  spyOn(localStorage, 'setItem');
  TestBed.configureTestingModule({
    providers: [UpdatesModalStore]
  });
});
  • 延迟localStorage操作时机:避免在服务构造函数或APP_INITIALIZER中立即执行localStorage读写,改为在组件首次调用服务方法时执行,确保服务已完成初始化。

  • 排查隐性依赖:检查UpdateNotificationStorageEnum所在组件是否依赖UpdatesModalStore,若有则调整组件的依赖注入方式,或在测试中先模拟该组件。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 22:47:16