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

如何为combineLatest Observable编写大理石测试?解决测试报错

Jasmine-Marbles测试Observable时的Notification类型错误排查与修复

问题场景

我实现了一个LoadingService,通过RxJS的combineLatest合并两个Observable<boolean>,并对执行结果做逻辑或操作:

interface LoadingEventEmitter {
  isLoading$: Observable<boolean>
}

export class LoadingService {
  isLoading$: Observable<boolean>;

  constructor(
    private requests: LoadingEventEmitter,
    private lazyRoutes: LoadingEventEmitter
  ) {
    this.isLoading$ = combineLatest([
      this.requests.isLoading$,
      this.lazyRoutes.isLoading$,
    ]).pipe(
      map(
        ([requestsLoading, lazyRoutesLoading]) =>
          requestsLoading || lazyRoutesLoading
      )
    );
  }
}

为验证逻辑正确性,我用jasmine-marbles编写了测试用例,但测试执行失败:

fdescribe('LoadingService', () => {
  const createLoadingService = (
    requests$: Observable<boolean>,
    lazyLoading$: Observable<boolean>
  ) => {
    const mockRequests = { isLoading$: requests$ };
    const mockLazyLoading = { isLoading$: lazyLoading$ };

    return new LoadingService(mockRequests, mockLazyLoading);
  };

  const values = { t: true, f: false };

  it('isLoading$ should be true when at least one of the sources emits true', () => {
    const a = cold('---t---f---f---', values); // First source
    const b = cold('f----t---------', values); // Second source
    const c = cold('---t-t-t---t---', values); // Result

    const service = createLoadingService(a, b);

    expect(service.isLoading$).toBeObservable(c);
  });
});

错误信息

测试抛出如下类型不匹配错误:

Expected $[0].notification to be a kind of Notification, but was Object({ kind: 'N', value: true, error: undefined }).
Expected $[1].notification to be a kind of Notification, but was Object({ kind: 'N', value: true, error: undefined }).
Expected $[2].notification to be a kind of Notification, but was Object({ kind: 'N', value: true, error: undefined }).
Expected $[3].notification to be a kind of Notification, but was Object({ kind: 'N', value: true, error: undefined }).

错误含义

这个错误核心原因是jasmine-marbles与RxJS版本不兼容:

  • 早期版本的jasmine-marbles依赖RxJS内置的Notification类包装事件通知
  • 适配RxJS 7+的新版本jasmine-marbles,改用普通JavaScript对象(格式为{ kind: 'N', value: ... })表示通知
  • 测试环境中,toBeObservable断言期望接收RxJS的Notification实例,但实际从Observable得到的是普通对象,因此触发类型不匹配错误。

修复方案

方案1:升级jasmine-marbles到兼容版本

如果项目使用RxJS 7及以上版本,直接升级jasmine-marbles到最新兼容版本即可解决问题:

npm install jasmine-marbles@latest --save-dev

升级后原有测试代码无需修改,断言会自动适配新的通知格式。

方案2:手动转换通知格式(适用于无法升级依赖的场景)

通过RxJS的materialize()操作符,将Observable事件流转换为标准通知流,确保断言双方格式一致:

  1. 导入materialize操作符:
import { materialize } from 'rxjs/operators';
  1. 修改测试中的断言逻辑:
it('isLoading$ should be true when at least one of the sources emits true', () => {
  const a = cold('---t---f---f---', values);
  const b = cold('f----t---------', values);
  const c = cold('---t-t-t---t---', values);

  const service = createLoadingService(a, b);

  // 同时转换实际流和预期流为通知格式
  expect(service.isLoading$.pipe(materialize())).toBeObservable(c.pipe(materialize()));
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 12:50:19