Angular单元测试中如何正确Mock含Subscribe的API调用?
解决Angular组件单元测试中Mock API调用的subscribe错误
问题原因
- 你创建的
HttpClientServiceSpy对象默认返回undefined,组件调用getFoamType_ColorList后尝试调用subscribe(),直接触发"Cannot read properties of undefined (reading 'subscribe')"错误。 - 后续尝试的Mock方式不符合要求:
getFoamType_ColorList实际返回Angular的Observable,而非简单带subscribe方法的对象,TypeScript类型检查会直接报错。
解决方案
需要让Spy返回模拟的Observable,用rxjs的of()操作符创建同步Observable,且要在测试初始化阶段就配置好Spy的返回值(因为fixture.detectChanges()会触发ngOnInit,提前调用服务方法)。
修改后的完整测试代码
import { ComponentFixture, TestBed } from "@angular/core/testing"; import { FormsModule, ReactiveFormsModule } from "@angular/forms"; import { RouterTestingModule } from '@angular/router/testing'; import { HttpClientService } from "@service/http-client.service"; import { ToastrService } from "ngx-toastr"; import { MaterialModule } from "src/app/material/material.module"; import { PrepCardComponent } from "./prep-card.component"; // 引入of操作符创建模拟Observable import { of } from 'rxjs'; describe('PrepCardComponent', () => { let component: PrepCardComponent; let fixture: ComponentFixture<PrepCardComponent>; let httpSpy: jasmine.SpyObj<HttpClientService>; let toastrSpy: jasmine.SpyObj<ToastrService>; beforeEach(async () => { // 创建服务Spy对象 const httpSpyObj = jasmine.createSpyObj('HttpClientService', ['getFoamType_ColorList']); const toastrSpyObj = jasmine.createSpyObj('ToastrService', ['success', 'error']); // 模拟服务返回的响应数据 const mockResponse = { message: 'success', data: { foamTypesAndColors: { foamTypes: ['PU', 'EPS'], colors: ['Black', 'White'] } } }; // 配置Spy返回模拟的Observable httpSpyObj.getFoamType_ColorList.and.returnValue(of(mockResponse)); await TestBed.configureTestingModule({ declarations: [PrepCardComponent], imports: [ RouterTestingModule, FormsModule, ReactiveFormsModule, MaterialModule ], providers: [ { provide: HttpClientService, useValue: httpSpyObj }, { provide: ToastrService, useValue: toastrSpyObj }, ] }).compileComponents(); fixture = TestBed.createComponent(PrepCardComponent); component = fixture.componentInstance; // 保存Spy引用,用于后续断言 httpSpy = TestBed.inject(HttpClientService) as jasmine.SpyObj<HttpClientService>; toastrSpy = TestBed.inject(ToastrService) as jasmine.SpyObj<ToastrService>; // 触发ngOnInit,此时Spy已配置好返回值 fixture.detectChanges(); }); it('should initialize component successfully', () => { expect(component).toBeTruthy(); }); it('should call getFoamType_ColorList with "ABC" on init', () => { expect(httpSpy.getFoamType_ColorList).toHaveBeenCalledWith('ABC'); }); it('should populate foamTypeList and colorList after service call', () => { expect(component.foamTypeList).toEqual(['PU', 'EPS']); expect(component.colorList).toEqual(['Black', 'White']); }); });
关键修改点
- 引入
of操作符:从rxjs导入of,创建同步Observable模拟API响应,确保组件能正常调用subscribe。 - 提前配置Spy返回值:在
beforeEach中创建Spy后立即设置返回值,避免ngOnInit调用服务时返回undefined。 - 模拟真实响应结构:按照服务返回的类型结构构建
mockResponse,保证组件的回调逻辑能正确解析数据。 - 保存Spy引用:通过
TestBed.inject获取Spy实例,方便后续测试用例验证服务调用情况。
为什么之前的尝试失败
- 初始测试中未配置Spy返回值,默认返回
undefined,导致subscribe()调用失败。 - 后续尝试的
{subscribe: () => {}}不是标准Observable类型,TypeScript类型检查不通过,且无法触发组件的回调逻辑。
内容的提问来源于stack exchange,提问作者avishekdr
相关产品推荐
相关产品推荐

