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

如何测试Angular中返回Promise内的subscribe函数?

解决Angular ConfigService Promise测试超时/错误通过问题

你的测试出现超时或错误通过的核心原因有两个:一是没有模拟HTTP请求的响应,导致Promise一直处于pending状态;二是测试未正确处理异步逻辑,Jasmine会直接结束测试而不等待Promise resolve。另外你的服务代码里还有两个小问题需要先修正。

第一步:修正服务代码

服务里的displayJsonFile未定义,应该作为参数传入load方法,同时静态属性appSettings要正确引用类本身:

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';

export interface IAppConfig {
  env: {
    name: string;
  };
  features: {
    [key: string]: boolean;
  };
}

@Injectable({
  providedIn: 'root',
})
export class ConfigService {
  static appSettings: IAppConfig;

  constructor(private http: HttpClient) {}

  load(displayJsonFile: boolean) { // 接收参数控制加载哪个文件
    const jsonFile = displayJsonFile
      ? `../../../../assets/.json1`
      : `../../../../assets/.json2`;

    return new Promise<IAppConfig>((resolve) => {
      this.http.get<IAppConfig>(jsonFile).subscribe((res) => {
        ConfigService.appSettings = res; // 正确引用静态属性
        resolve(res);
      });
    });
  }
}

第二步:修正测试代码

测试需要处理异步逻辑,同时用HttpTestingController模拟HTTP响应:

import { TestBed, async } from '@angular/core/testing';
import { ConfigService } from './config-service.service';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { IAppConfig } from './config-service.service';

describe('ConfigService', () => {
  let service: ConfigService;
  let httpMock: HttpTestingController;

  const mockJson1Config: IAppConfig = {
    env: {
      name: 'json1',
    },
    features: {
      feature1: true,
      feature2: true,
    },
  };

  const mockJson2Config: IAppConfig = {
    env: {
      name: 'json2',
    },
    features: {
      feature1: false,
      feature2: true,
    },
  };

  beforeEach(() => {
    TestBed.configureTestingModule({ imports: [HttpClientTestingModule] });
    service = TestBed.inject(ConfigService);
    httpMock = TestBed.inject(HttpTestingController);
  });

  afterEach(() => {
    // 验证所有HTTP请求都已被处理,避免内存泄漏
    httpMock.verify();
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });

  it('should load .json1 when displayJsonFile is true', async () => {
    // 调用load方法,传入参数
    const result = await service.load(true);
    
    // 匹配对应的HTTP请求
    const req = httpMock.expectOne('../../../../assets/.json1');
    // 模拟返回数据
    req.flush(mockJson1Config);

    // 验证Promise结果和静态属性
    expect(result).toEqual(mockJson1Config);
    expect(ConfigService.appSettings).toEqual(mockJson1Config);
  });

  it('should load .json2 when displayJsonFile is false', (done) => {
    // 也可以用done回调处理异步
    service.load(false).then(res => {
      expect(res).toEqual(mockJson2Config);
      expect(ConfigService.appSettings).toEqual(mockJson2Config);
      done(); // 通知Jasmine测试完成
    });

    const req = httpMock.expectOne('../../../../assets/.json2');
    req.flush(mockJson2Config);
  });
});

关键说明

  1. 异步处理:用async/await或done回调让Jasmine等待Promise完成,避免测试提前结束。
  2. HTTP模拟:调用load后必须用httpMock.expectOne匹配请求URL,再用flush返回模拟数据,这样subscribe才会触发,Promise才能resolve。
  3. 验证请求:afterEach里的httpMock.verify()确保没有未处理的HTTP请求,避免测试污染。
  4. 类型安全:给http.get和Promise加上泛型<IAppConfig>,避免any类型导致的潜在问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 12:23:11