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

Angular测试中Navigator InjectionToken触发ReferenceError问题求助

解决Angular单元测试中Navigator未定义的问题

问题背景

我为Navigator和Window实现了以下抽象注入令牌,以便在测试时模拟依赖:

export const NAVIGATOR: InjectionToken<Navigator> = new InjectionToken<Navigator>(
  'An abstraction over window.navigator object',
  {
    factory: () => inject(WINDOW).navigator,
  },
);

WINDOW注入令牌

import { inject, InjectionToken } from '@angular/core';
import { DOCUMENT } from '@angular/common';

export const WINDOW: InjectionToken<Window> = new InjectionToken<Window>(
  'An abstraction over global window object',
  {
    factory: () => {
      const { defaultView }: any = inject(DOCUMENT);

      if (!defaultView) {
        throw new Error('Window is not available');
      }

      return defaultView;
    },
  },
);

服务中注入NAVIGATOR

export class MyService {
  constructor(
    @Inject(NAVIGATOR) private navigator: Navigator,
  ) {}
}

测试错误信息

运行单元测试时抛出如下错误:

ReferenceError: Navigator is not defined

      3 |   constructor(
    > 4 |     @Inject(NAVIGATOR) private navigator: Navigator,
        |                                           ^
      6 |   ) {}

当前tsconfig配置

tsconfig.json

{
  "compilerOptions": {
    "allowJs": true,
    "allowUnreachableCode": false,
    "allowUnusedLabels": false,
    "target": "es2015",
    "module": "ES2020",
    "moduleResolution": "node",
    "declaration": false,
    "removeComments": true,
    "noLib": false,
    "experimentalDecorators": true,
    "sourceMap": true,
    "pretty": true,
    "noImplicitAny": false,
    "noImplicitReturns": false,
    "noImplicitUseStrict": false,
    "noImplicitThis": false,
    "noFallthroughCasesInSwitch": true,
    "resolveJsonModule": true,
    "strictNullChecks": true,
    "strictPropertyInitialization": false,
    "strict": true,
    "skipLibCheck": true,
    "lib": [
      "es6",
      "dom",
      "es2017"
    ],
    "typeRoots": [
      "./src/types",
      "node_modules/@types"
    ],
    "types": [
      "jest",
      "node"
    ],
    "baseUrl": "./src"
  },
  "angularCompilerOptions": {
    "enableIvy": true
  },
  "compileOnSave": true,
  "exclude": [
    "node_modules",
    "dist",
    "assets",
    "translations"
  ]
}

tsconfig.spec.json

{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "outDir": "../out-tsc/spec",
    "module": "commonjs",
    "emitDecoratorMetadata": true,
    "types": [
      "jest",
      "node"
    ]
  },
  "files": [
    "polyfills.ts"
  ],
  "include": [
    "**/*.spec.ts",
    "**/*.d.ts"
  ]
}

解决方案

1. 确保测试配置包含DOM类型库

Jest运行在Node环境,默认不提供DOM相关的全局类型。虽然主tsconfig.json包含了dom库,但测试配置可能未正确继承该设置。修改tsconfig.spec.json,在compilerOptions中显式添加dom库:

"compilerOptions": {
  "outDir": "../out-tsc/spec",
  "module": "commonjs",
  "emitDecoratorMetadata": true,
  "lib": ["es6", "dom", "es2017"],
  "types": [
    "jest",
    "node"
  ]
}

2. 在测试模块中模拟NAVIGATOR

Node环境没有真实的Navigator实例,必须在测试中提供模拟对象。示例测试代码如下:

import { TestBed } from '@angular/core/testing';
import { MyService } from './my.service';
import { NAVIGATOR } from './tokens';

describe('MyService', () => {
  let service: MyService;
  let mockNavigator: Partial<Navigator>;

  beforeEach(() => {
    // 按需模拟Navigator的属性和方法
    mockNavigator = {
      userAgent: 'test-browser-agent',
      geolocation: {
        getCurrentPosition: jest.fn()
      }
    };

    TestBed.configureTestingModule({
      providers: [
        MyService,
        { provide: NAVIGATOR, useValue: mockNavigator }
      ]
    });
    service = TestBed.inject(MyService);
  });

  it('should initialize successfully', () => {
    expect(service).toBeTruthy();
  });
});

3. 切换到JSDOM测试环境(可选)

如果需要更完整的DOM环境支持,可以改用jest-environment-jsdom:

  1. 安装依赖:
npm install --save-dev jest-environment-jsdom
  1. 在jest.config.js中配置测试环境:
module.exports = {
  testEnvironment: 'jsdom'
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 15:13:20