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

Angular Jasmine Karma测试中如何Mock SpyOn并返回Observable

Angular 14 中用 Jasmine/Karma 测试 Observable 并模拟数据的两种方法

先梳理你现有测试代码的问题:

  • userModel 未初始化,且 getUserPrefer 返回的是 Observable<ApiResp<UserModel>>,直接返回 userModel 类型不匹配
  • 测试未订阅 Observable,也没有断言验证结果
  • 未 mock 依赖的 CService,运行时会因找不到 aConfig 报错
  • 测试模块应导入 HttpClientTestingModule 而非 HttpClientModule,避免发起真实 HTTP 请求

下面提供两种可行的解决方案:


方案一:直接 SpyOn 服务方法返回模拟 Observable

这种方式适合快速测试依赖该服务的组件,直接替换服务方法的返回值:

import { TestBed } from '@angular/core/testing';
import { UserService } from './UserService';
import { Observable, of } from 'rxjs';
import { ApiResp, UserModel } from './ApiResp';
import { CService } from './CService';

describe('UserService', () => {
  let service: UserService;
  // 模拟符合结构的响应数据
  const mockUserResp: ApiResp<UserModel> = {
    status: 200,
    timestamp: new Date().toISOString(),
    payload: {
      email: 'john@test.com',
      id: '123'
    }
  };

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        UserService,
        // Mock CService,避免依赖真实配置
        {
          provide: CService,
          useValue: {
            aConfig: { customer: 'https://mock-api.com/' }
          }
        }
      ]
    });
    service = TestBed.inject(UserService);
  });

  it('should get UserPreference with mocked Observable', (done) => {
    // SpyOn 并返回模拟的 Observable
    spyOn(service, 'getUserPrefer').and.returnValue(of(mockUserResp));

    // 调用方法并订阅结果
    service.getUserPrefer('newuserjohn').subscribe(resp => {
      // 断言返回数据符合预期
      expect(resp.status).toBe(200);
      expect(resp.payload?.email).toBe('john@test.com');
      // 验证方法被调用过
      expect(service.getUserPrefer).toHaveBeenCalledWith('newuserjohn');
      done(); // 异步测试需调用 done 通知完成
    });
  });
});

方案二:用 HttpClientTestingModule 模拟 HTTP 请求(更推荐)

这种方式会测试服务的真实逻辑(是否正确发起 HTTP 请求),而非直接替换方法:

import { TestBed } from '@angular/core/testing';
import { UserService } from './UserService';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { ApiResp, UserModel } from './ApiResp';
import { CService } from './CService';

describe('UserService', () => {
  let service: UserService;
  let httpMock: HttpTestingController;
  const mockBaseUrl = 'https://mock-api.com/';
  const mockUserResp: ApiResp<UserModel> = {
    status: 200,
    timestamp: new Date().toISOString(),
    payload: {
      email: 'john@test.com',
      id: '123'
    }
  };

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      providers: [
        UserService,
        {
          provide: CService,
          useValue: { aConfig: { customer: mockBaseUrl } }
        }
      ]
    });
    service = TestBed.inject(UserService);
    httpMock = TestBed.inject(HttpTestingController);
  });

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

  it('should send GET request and return user preference', (done) => {
    const userId = 'newuserjohn';
    const expectedUrl = `${mockBaseUrl}v1/getuser/${userId}`;

    // 调用服务方法
    service.getUserPrefer(userId).subscribe(resp => {
      expect(resp).toEqual(mockUserResp);
      expect(resp.payload?.id).toBe('123');
      done();
    });

    // 捕获并匹配 HTTP 请求
    const req = httpMock.expectOne(expectedUrl);
    // 验证请求方法是 GET
    expect(req.request.method).toBe('GET');
    // 返回模拟响应
    req.flush(mockUserResp);
  });
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 04:43:18