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

Angular如何为new Map()创建的functionMap、ComponentMap编写单元测试

TourStoreServiceService 单元测试编写方案

测试基于Angular官方TestBed测试工具+Jest/Jasmine语法实现,核心逻辑是隔离外部依赖,逐一验证内部Map读写、状态变更、外部依赖调用的行为是否符合预期。

前置测试环境配置

首先mock服务依赖的StorageService,避免真实存储操作影响用例独立性:

import { TestBed } from '@angular/core/testing';
import { TourStoreServiceService } from './tour-store-service.service';
import { LOCAL_STORAGE } from './your-injection-token.path'; // 替换为项目中LOCAL_STORAGE注入令牌的实际路径
import { Type } from '@angular/core';

// 模拟存储服务
class MockStorageService {
  set = jest.fn(); // 若使用Jasmine,替换为jasmine.createSpy('set')
}

describe('TourStoreServiceService', () => {
  let service: TourStoreServiceService;
  let storage: MockStorageService;

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        TourStoreServiceService,
        { provide: LOCAL_STORAGE, useClass: MockStorageService }
      ]
    });
    service = TestBed.inject(TourStoreServiceService);
    storage = TestBed.inject(LOCAL_STORAGE) as unknown as MockStorageService;
    // 每个用例执行前清空mock调用记录
    jest.clearAllMocks(); // Jasmine框架替换为.calls.reset()
  });
});

测试核心原则:

  • 用例之间完全隔离,每个用例执行前重新初始化服务实例,避免Map残留数据干扰断言
  • 所有外部依赖全部用mock替代,不操作真实本地存储,保证测试执行速度和结果稳定性
  • 涉及随机值生成的逻辑通过桩代码固定返回值,避免随机结果导致的测试偶发失败

具体测试用例实现

1. 函数注册与查询逻辑(对应functionMap操作)

覆盖registerFnRef、getFnByKey两个方法的核心场景:

it('registerFnRef 需将传入的函数以指定key存入内部functionMap', () => {
  const testKey = 'testFunction';
  const mockFn = () => 'test result';
  service.registerFnRef(testKey, mockFn);
  // 断言存入的函数和原函数引用一致
  expect(service.getFnByKey(testKey)).toBe(mockFn);
});

it('getFnByKey 查询不存在的key时需返回undefined', () => {
  expect(service.getFnByKey('nonExistFnKey')).toBeUndefined();
});

it('registerFnRef 重复注册同一个key时需覆盖原有函数引用', () => {
  const testKey = 'duplicateFnKey';
  const firstMockFn = () => 1;
  const secondMockFn = () => 2;
  service.registerFnRef(testKey, firstMockFn);
  service.registerFnRef(testKey, secondMockFn);
  expect(service.getFnByKey(testKey)).toBe(secondMockFn);
});

2. 组件注册与查询逻辑(对应componentMap操作)

覆盖registerComponent、getComponentByKey两个方法的核心场景:

it('registerComponent 需将传入的组件类以指定key存入内部componentMap', () => {
  const testKey = 'testComponent';
  // 模拟测试组件类
  class MockTestComponent {}
  service.registerComponent(testKey, MockTestComponent as Type<unknown>);
  expect(service.getComponentByKey(testKey)).toBe(MockTestComponent);
});

it('getComponentByKey 查询不存在的key时需返回undefined', () => {
  expect(service.getComponentByKey('nonExistComponentKey')).toBeUndefined();
});

it('registerComponent 重复注册同一个key时需覆盖原有组件引用', () => {
  const testKey = 'duplicateComponentKey';
  class FirstMockComponent {}
  class SecondMockComponent {}
  service.registerComponent(testKey, FirstMockComponent as Type<unknown>);
  service.registerComponent(testKey, SecondMockComponent as Type<unknown>);
  expect(service.getComponentByKey(testKey)).toBe(SecondMockComponent);
});

3. 会话ID生成逻辑(对应generateSessionId方法)

同时验证内部状态赋值、存储调用两个行为,通过桩代码替换uuid生成逻辑,避免随机值导致断言不稳定:

it('generateSessionId 需生成新会话ID、赋值给内部状态同时写入本地存储', () => {
  const fixedTestSessionId = 'fixed-session-id-001';
  // 替换内部sessionGenerator的返回值,屏蔽uuid随机性
  jest.spyOn(service as any, 'sessionGenerator', 'get').mockReturnValue(() => fixedTestSessionId);
  // Jasmine框架替换为:
  // spyOnProperty(service as any, 'sessionGenerator', 'get').and.returnValue(() => fixedTestSessionId);

  service.generateSessionId();

  // 验证存储方法调用参数正确
  expect(storage.set).toHaveBeenCalledTimes(1);
  expect(storage.set).toHaveBeenCalledWith('TOUR_SESSION_ID', fixedTestSessionId);
  // 验证内部sessionId状态赋值正确
  expect((service as any).sessionId).toBe(fixedTestSessionId);
});

说明:用例中通过as any访问类的私有成员是单元测试的常规操作,不需要为了测试特意修改类成员的访问修饰符;如果项目规范严格禁止访问私有成员,可以给服务类增加一个只读的sessionId getter对外暴露值,再通过getter完成断言即可。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 14:51:35