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

Angular中Jest单元测试异常:Observable属性订阅无值但方法形式正常

问题原因

你的currentContent$是类的实例属性,在TestService实例创建的瞬间就会执行this.getContent()并初始化这个Observable。而你是先创建了service实例,再用jest.spyOn去mockgetContent方法——这时候currentContent$已经绑定了原始getContent返回的Observable,mock根本没作用到它身上,所以订阅自然收不到值。

而testMethod()是调用时才执行this.getContent(),这时候mock已经生效,所以能正常拿到mock的Observable,订阅就有输出。

解决方案

方案1:先mock方法,再创建服务实例

把mock操作放在实例化service之前,这样初始化currentContent$时就会用mock后的getContent:

it('should emit values only if not deeply equal', () => {
  const mockData1 = {value:'Path1'};
  const mockData2 = {value:'Path2'};
  const mockData3 = {value:'Path3'};

  // 先mock原型上的getContent方法
  jest.spyOn(TestService.prototype, 'getContent').mockReturnValue(of(mockData1, mockData2, mockData3, mockData3));
  
  // 再创建service实例
  const service = new TestService();

  const emittedValues: any[] = [];
  service.currentContent$.subscribe((value) => {
    emittedValues.push(value);
  });

  // of是同步Observable,直接断言即可,无需setTimeout和done
  expect(emittedValues).toEqual([mockData1, mockData2, mockData3]);
});

方案2:将currentContent$改为getter延迟初始化

修改TestService,把currentContent$改成getter属性,这样每次访问它时才会调用getContent(),mock后再访问就会生效:

export class TestService {  
  // 改成getter
  get currentContent$(): Observable<any> {
    return this.getContent().pipe(
      distinctUntilChanged((p, c) => isEqual(p, c))
    );
  }

  constructor() {}

  getContent(): Observable<any> {
    return of('some test return value');
  }
}

测试代码可以保持你原来的顺序(先实例化,再mock,再订阅):

it('should emit values only if not deeply equal', () => {
  const service = new TestService();
  const mockData1 = {value:'Path1'};
  const mockData2 = {value:'Path2'};
  const mockData3 = {value:'Path3'};

  jest.spyOn(service, 'getContent').mockReturnValue(of(mockData1, mockData2, mockData3, mockData3));

  const emittedValues: any[] = [];
  service.currentContent$.subscribe((value) => {
    emittedValues.push(value);
  });

  expect(emittedValues).toEqual([mockData1, mockData2, mockData3]);
});
额外注意点
  1. 原测试里的断言错误:你mock的是{value:'Path1'}这类对象,所以断言应该匹配对象数组,而不是字符串数组['Path1','Path2','Path3'];如果要断言value字段,订阅时可以pushvalue.value。
  2. of创建的Observable是同步发射值的,完全不需要用setTimeout和done,直接订阅后就能断言。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 11:58:20