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

Angular单元测试中如何正确Mock含Subscribe的API调用?

解决Angular组件单元测试中Mock API调用的subscribe错误

问题原因

  • 你创建的HttpClientService Spy对象默认返回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']);
    });
});

关键修改点

  1. 引入of操作符:从rxjs导入of,创建同步Observable模拟API响应,确保组件能正常调用subscribe。
  2. 提前配置Spy返回值:在beforeEach中创建Spy后立即设置返回值,避免ngOnInit调用服务时返回undefined。
  3. 模拟真实响应结构:按照服务返回的类型结构构建mockResponse,保证组件的回调逻辑能正确解析数据。
  4. 保存Spy引用:通过TestBed.inject获取Spy实例,方便后续测试用例验证服务调用情况。

为什么之前的尝试失败

  • 初始测试中未配置Spy返回值,默认返回undefined,导致subscribe()调用失败。
  • 后续尝试的{subscribe: () => {}}不是标准Observable类型,TypeScript类型检查不通过,且无法触发组件的回调逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 18:58:05