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

Angular从Jasmine转Jest做单元测试:Observable服务方法测试报错

问题:Angular单元测试从Jasmine切换到Jest后,Observable方法测试报错

我正在将Angular单元测试从Jasmine切换到Jest,更新后的服务测试代码如下:

....
describe( 'My Test', () => { 
   let myService: any;
   ...
   
   beforeEach(() => {
      myService = {
         theMethodInTheService: jest.fn()
      }
      TestBed.configureTestingModule({
         ....
      })
      myService = TestBed.inject(MyService);
   })
   it('should fetch data from user endpoint', () => {
       myService.theMethodInTheService.subscribe( ( res:any ) => {
            expect(res).toBe('theExpectedResponse');
       });
       const req = httpTestingController.expectOne(theExpectedUrl);
       expect(req.request.method).toEqual('GET');
       req.flush('theExpectedResponse');
   });
})

测试失败,报错信息:

TypeError: myService.theMethodInTheService.subscribe is not a function

theMethodInTheService返回一个Observable,请问如何使用Jest进行该场景的测试?


解决方案

报错原因是你创建的mock方法theMethodInTheService: jest.fn()默认返回undefined,并非Observable,因此调用subscribe时会报错。根据测试需求,有两种可行方案:

方案1:测试真实服务的HTTP请求(推荐用于验证请求逻辑)

如果需要验证服务发起的HTTP请求细节(URL、请求方法等),并测试响应处理,直接使用真实服务配合HttpTestingController即可:

import { TestBed } from '@angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { MyService } from './my.service';

describe('My Test', () => {
  let myService: MyService;
  let httpTestingController: HttpTestingController;
  const theExpectedUrl = '目标请求URL';

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule], // 必须导入该模块以支持HTTP测试
      providers: [MyService] // 注入真实服务
    });

    myService = TestBed.inject(MyService);
    httpTestingController = TestBed.inject(HttpTestingController);
  });

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

  it('should fetch data from user endpoint', () => {
    myService.theMethodInTheService().subscribe((res: any) => {
      expect(res).toBe('theExpectedResponse');
    });

    // 拦截预期的HTTP请求
    const req = httpTestingController.expectOne(theExpectedUrl);
    expect(req.request.method).toEqual('GET');
    // 返回模拟响应
    req.flush('theExpectedResponse');
  });
});

方案2:Mock服务方法返回Observable(适合隔离依赖测试)

如果不需要验证HTTP请求细节,仅需mock服务方法的返回值,只需让jest.fn()返回一个Observable即可:

import { TestBed } from '@angular/core/testing';
import { MyService } from './my.service';
import { of } from 'rxjs';

describe('My Test', () => {
  let myService: jest.Mocked<MyService>;

  beforeEach(() => {
    // 创建mock服务,指定方法返回Observable
    const mockService = {
      theMethodInTheService: jest.fn(() => of('theExpectedResponse'))
    };

    TestBed.configureTestingModule({
      providers: [
        { provide: MyService, useValue: mockService }
      ]
    });

    // 类型转换以获得Jest mock的类型提示
    myService = TestBed.inject(MyService) as jest.Mocked<MyService>;
  });

  it('should handle data from service method', () => {
    myService.theMethodInTheService().subscribe((res: any) => {
      expect(res).toBe('theExpectedResponse');
    });

    // 验证服务方法已被调用
    expect(myService.theMethodInTheService).toHaveBeenCalled();
  });
});

关键提示

  • jest.fn()可传入函数参数指定返回值,这里用RxJS的of()创建一个发射指定值的Observable。
  • 使用jest.Mocked<MyService>替代any类型,能获得更友好的类型提示,避免类型错误。

内容的提问来源于stack exchange,提问作者k.vincent

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 18:23:17