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

Angular测试报错:Cannot read properties of undefined (reading 'baseURL')

Karma测试HeaderComponent时出现TypeError:无法读取undefined的baseURL属性

运行HeaderComponent的默认Karma测试用例时,抛出TypeError,提示无法读取undefined的baseURL属性。错误根源是DailyplanningService构造函数中调用this.configService.getConfig().baseURL,且多个组件测试均出现此问题。


dailyplanningservice.ts

import { Injectable } from '@angular/core';
import { map } from 'rxjs/operators';
import { AppConfigService } from 'src/app/services/config/app-config.service';

import { Observable,} from 'rxjs';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { RequisitionProposalPostModel } from '../model/requisition-proposal';
var jsonData = './assets/data/test.json';

interface JsonResponse {
  status: number;
  message: any;
  data: object;
  dataList:Array<any>;
}

@Injectable({
  providedIn: 'root'
})
export class DailyplanningService {


  private handleResponse(response: JsonResponse) {
    return response;
  }

  serviceBase = "";
  baseUrl = "";
  constructor(private _http: HttpClient,
    private configService: AppConfigService) {

    this.baseUrl = this.configService.getConfig().baseURL;
    this.serviceBase = this.configService.getConfig().serviceBase;
  }

  getReportData(imoCode: string) {
    return this._http.get<any>(this.baseUrl + this.serviceBase + `dailyplan/noonReport?imoCode=` + imoCode);
  }

  getVesselData(imoCode: string) {   
    return this._http.get(this.baseUrl + this.serviceBase + `dailyplan/vesselCard?imoCode=` + imoCode);
  }

  getVesselCardData(imoCode: any): Observable<Object> {
    return  this._http.get(this.baseUrl + this.serviceBase + `dailyplan/port?imoCode=` + imoCode)
  } 

  submitrfqProposal(requisitionProposalPostModel: RequisitionProposalPostModel): Observable<any> {
    return this._http.post<any>( this.baseUrl + this.serviceBase + 'dailyplan/requisition', requisitionProposalPostModel);
  }

  getRequisitionProposal(imoCode:any, voyageId:any): Observable<any> {

    return this._http.get<any>(this.baseUrl + this.serviceBase + `dailyplan/requisitionproposal?imoCode=` + imoCode +"&voyagePassageId="+voyageId);



  }


  // public getVesselData1(imoCode: any) {
  //   return this.http.get(this.baseUrl + this.serviceBase + `dailyplan/vesselCard?imoCode=` + imoCode).pipe(
  //     catchError(
  //       (error: HttpErrorResponse, caught: Observable<HttpEvent<unknown>>) => {
  //         console.error(
  //           'the component has caught an error, process it here',
  //           error
  //         );
  //         this.error = error;
  //         return of();
  //       }
  //     )
  //   );
  // }


  doRequest(endpointURL: string,
    method: string, data?: any,
    parms?: any, headers?: HttpHeaders) {

    const httpOptions = {
      headers: headers ? headers : new HttpHeaders({
        'Content-Type': 'application/json',
      }),
      body: !data ? {} : JSON.stringify(data),
      parms: parms
    }

    return this._http
      .request<JsonResponse>(method, this.baseUrl + this.serviceBase + `${endpointURL}`, httpOptions)
      .pipe(
        map(response =>
          this.handleResponse(response))
      );


  }
}

HeaderComponent.ts

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { CognitoService } from 'src/app/modules/auth/cognito.service';
import { DailyplanningService } from 'src/app/modules/pages/daily-planning/service/dailyplanning.service';
import { CommonService } from 'src/app/services/common/common.service';

@Component({
  selector: 'app-header',
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.scss'],
})
export class HeaderComponent implements OnInit {

  responseLst: any = [];
  dailyplanningURL = "";
  bell_icon: boolean = false;

  constructor(public router: Router,
    private _service: DailyplanningService,
    private common: CommonService,
    public cognitoService: CognitoService) {

    this.common.shareDailyPlaningURL.subscribe((res: any) => {
      this.dailyplanningURL = res;
    });


    this.common.shareVesselData.subscribe((res: any) => {
      if (res) {
        this.responseLst = res;
      } else {
        this.getVesselData();
      }

    });

  }

  userAdminUrlActive = "";
  loading: boolean = false;
  collapsed = true;

  ngOnInit(): void {
console.log();

  }

  
  toggleCollapsed(): void {
    this.collapsed = !this.collapsed;
  }


  goDashboard() {
    this.dailyplanningURL = '';
    this.isShowPopIn = false;
    this.router.navigate(['/pages/dashboard']);
  }

  changeUrl() {
    this.dailyplanningURL = '';
    this.isShowPopIn = false;
  }

  logout() {
    this.cognitoService.signOut();
    this.router.navigate(['/login']);
  }
  userAccountClick() {
    this.userAdminUrlActive = '/pages/useraccount';
    this.router.navigate(['/pages/useraccount']);
  }
  vesselMgtClick() {
    this.userAdminUrlActive = '/pages/vesselmanagement';
    this.router.navigate(['/pages/vesselmanagement']);
  }


  gotoDailyplanning(data: any) {
    this.isShowPopIn = false;
    sessionStorage.setItem("vesselData", JSON.stringify(data));

    this.router.navigateByUrl('/pages/dashboard')
      .then(() => this.router.navigate(['/pages/dailyplanning']));
    this.dailyplanningURL = '/pages/dailyplanning';
  }

  isShowPopIn = false;
  showdailyPlanning() {
    if (this.isShowPopIn) {
      this.isShowPopIn = false;
    } else {
      if (this.responseLst.length == 0) {
        this.getVesselData();
      }
      setTimeout(() => {
        this.isShowPopIn = true;
      }, 500);

    }
  }

  getVesselData() {
    try {
      this.loading = true;
      let apiURL = "useradmin/vessels?email=" + sessionStorage.getItem('email')
      this.common.doRequest(apiURL, 'get').subscribe({
        next: (res: any) => {
          let result: any = res;
          this.responseLst = [];
          if (result) {
            if (result.status == 200) {
              result.dataList.forEach((item: any) => {
                let obj: any = new Object();
                Object.keys(item).forEach(function (key: string) {
                  if (item[key] == 0 || item[key] == null) {
                    obj[key] = "0";
                  } else {
                    obj[key] = item[key];
                  }

                });
                this.responseLst.push(obj);
                this.loading = false;
              });
            }
          }
        },
        error: (error: any) => {
          this.loading = false;
          console.log('Error in getVesselData:' + error);
        }
      });
    } catch (error: any) {
      this.loading = false;
      console.log('Error in getVesselData:' + error.message);
    }

  }


  
}

HeaderComponent.spec.ts

import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';

import { HeaderComponent } from './header.component';

describe('HeaderComponent', () => {
  let component: HeaderComponent;
  let fixture: ComponentFixture<HeaderComponent>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [RouterTestingModule,
                HttpClientTestingModule],
      declarations: [ HeaderComponent ],
      schemas: [CUSTOM_ELEMENTS_SCHEMA]
    })
    .compileComponents();
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(HeaderComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});

错误信息

TypeError: Cannot read properties of undefined (reading 'baseURL')
TypeError: Cannot read properties of undefined (reading 'baseURL')
    at new DailyplanningService (http://localhost:9876/_karma_webpack_/webpack:/src/app/modules/pages/daily-planning/service/dailyplanning.service.ts:32:50)
    at Object.DailyplanningService_Factory [as factory] (ng:///DailyplanningService/ɵfac.js:4:10)
    at R3Injector.hydrate (http://localhost:9876/_karma_webpack_/vendor.js:256012:35)
    at R3Injector.get (http://localhost:9876/_karma_webpack_/vendor.js:255831:33)
    at NgModuleRef.get (http://localhost:9876/_karma_webpack_/vendor.js:266387:33)
    at Object.get (http://localhost:9876/_karma_webpack_/vendor.js:266064:35)
    at lookupTokenUsingModuleInjector (http://localhost:9876/_karma_webpack_/vendor.js:247913:39)
    at getOrCreateInjectable (http://localhost:9876/_karma_webpack_/vendor.js:248025:12)
    at ɵɵdirectiveInject (http://localhost:9876/_karma_webpack_/vendor.js:258951:12)
    at NodeInjectorFactory.HeaderComponent_Factory [as factory] (ng:///HeaderComponent/ɵfac.js:4:81)

解决方案

问题核心是测试模块未提供AppConfigService的模拟实现,导致DailyplanningService初始化时configService.getConfig()返回undefined,进而读取baseURL时出错。需在测试配置中添加该服务的模拟,同时处理其他依赖的模拟:

修改测试代码

更新HeaderComponent.spec.ts,添加所有依赖服务的模拟:

import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppConfigService } from 'src/app/services/config/app-config.service';
import { CognitoService } from 'src/app/modules/auth/cognito.service';
import { CommonService } from 'src/app/services/common/common.service';
import { of } from 'rxjs';

import { HeaderComponent } from './header.component';

describe('HeaderComponent', () => {
  let component: HeaderComponent;
  let fixture: ComponentFixture<HeaderComponent>;
  let mockAppConfigService: jasmine.SpyObj<AppConfigService>;
  let mockCognitoService: jasmine.SpyObj<CognitoService>;
  let mockCommonService: jasmine.SpyObj<CommonService>;

  beforeEach(async () => {
    // 创建AppConfigService模拟,返回包含baseURL和serviceBase的配置对象
    mockAppConfigService = jasmine.createSpyObj('AppConfigService', ['getConfig']);
    mockAppConfigService.getConfig.and.returnValue({
      baseURL: 'http://mock-url.com',
      serviceBase: 'api/'
    });

    // 模拟CognitoService的signOut方法
    mockCognitoService = jasmine.createSpyObj('CognitoService', ['signOut']);

    // 模拟CommonService的Observable和doRequest方法
    mockCommonService = jasmine.createSpyObj('CommonService', ['doRequest']);
    mockCommonService.shareDailyPlaningURL = of('');
    mockCommonService.shareVesselData = of([]);
    mockCommonService.doRequest.and.returnValue(of({status:200, dataList:[]}));

    await TestBed.configureTestingModule({
      imports: [RouterTestingModule, HttpClientTestingModule],
      declarations: [ HeaderComponent ],
      schemas: [CUSTOM_ELEMENTS_SCHEMA],
      providers: [
        { provide: AppConfigService, useValue: mockAppConfigService },
        { provide: CognitoService, useValue: mockCognitoService },
        { provide: CommonService, useValue: mockCommonService }
      ]
    })
    .compileComponents();
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(HeaderComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});

说明

  • 给AppConfigService创建模拟对象,让getConfig返回包含baseURL和serviceBase的模拟配置,解决DailyplanningService初始化时的undefined问题。
  • 同时模拟HeaderComponent依赖的CognitoService和CommonService,避免其他依赖缺失导致的测试失败,确保测试能正常执行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 16:07:09