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

Angular服务单元测试报错:service.config未定义(main.js中)

Angular AppConfigService单元测试:config未定义错误排查

我编写了一个用于动态获取Angular应用配置的AppConfigService,单元测试中第一个验证服务实例创建的测试通过,但第二个验证config中apiUri值的测试失败,Karma抛出TypeError: service.config is undefined错误。

服务代码

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

@Injectable({
  providedIn: 'root'
})
export class AppConfigService {
  private appConfig: any;

  constructor(private http: HttpClient) { }

  async loadAppConfig(): Promise<any> {
    try {
      const response = await this.http.get('/assets/data/appConfig.json')
        .toPromise()
      this.appConfig = response;
      return this.appConfig;
    } catch (err) {
      return Promise.reject(err)
    }
  }

  get config(): any {
    return this.appConfig;
  }
}

原测试代码

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

import { AppConfigService } from './app-config.service';

describe('AppConfigService', () => {
  let service: AppConfigService;
  let client: HttpClient;

  beforeEach(() => {
    service = new AppConfigService(client)
  });

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

  it('should output correct apiUri', () => {
    expect(service.config.apiUri).toBe('http://api:8080/query');
    });
});

Karma报错信息

Firefox 102.0 (Linux x86_64) AppConfigService should output correct apiUri FAILED
    TypeError: service.config is undefined in main.js (line 46)
    501/<<@main.js:46:9
    invoke@polyfills.js:382:26
    onInvoke@vendor.js:102041:39
    invoke@polyfills.js:381:52
    run@polyfills.js:144:43
    runInTestZone@vendor.js:102321:34
    wrapTestInZone/<@vendor.js:102336:20
    <Jasmine>
    invokeTask@polyfills.js:416:31
    runTask@polyfills.js:188:47
    drainMicroTaskQueue@polyfills.js:592:35

错误原因

  • appConfig未初始化:appConfig私有变量初始值为undefined,仅在loadAppConfig()方法成功执行后才会被赋值。原测试未调用loadAppConfig(),直接访问service.config自然返回undefined,无法读取apiUri。
  • HttpClient未模拟:原测试将未初始化的client传入服务构造函数,即使调用loadAppConfig(),实际的http.get也会报错,无法获取配置。
  • 异步逻辑未处理:loadAppConfig()是异步方法,原测试未处理Promise异步流程,断言会在配置加载完成前执行,导致结果不符合预期。

修正后的测试代码

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

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

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

  afterEach(() => {
    // 验证所有HTTP请求均已处理
    httpMock.verify();
  });

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

  it('should output correct apiUri', async () => {
    // 模拟配置文件返回数据
    const mockConfig = { apiUri: 'http://api:8080/query' };
    
    // 调用异步加载方法
    const config = await service.loadAppConfig();
    
    // 捕获HTTP请求并返回模拟数据
    const req = httpMock.expectOne('/assets/data/appConfig.json');
    expect(req.request.method).toBe('GET');
    req.flush(mockConfig);
    
    // 断言配置值正确
    expect(config.apiUri).toBe('http://api:8080/query');
    expect(service.config.apiUri).toBe('http://api:8080/query');
  });
});

修正说明

  • 使用TestBed配合HttpClientTestingModule模拟HttpClient,避免发送真实HTTP请求,保证测试独立性。
  • 通过async/await处理loadAppConfig()的异步逻辑,确保断言在配置加载完成后执行。
  • 利用HttpTestingController捕获HTTP请求,返回预定义的模拟配置数据,精准控制测试场景。
  • 增加afterEach钩子验证所有HTTP请求均被处理,避免测试污染。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 21:18:22