如何测试服务上的一系列函数调用是否会从同一服务上的 Observable 产生预期的一系列发射值

How to test that a series of function calls on a service produce the expected series of emitted values from an Observable on that same service

我有以下服务:

export class MathService {
  private _total = new BehaviorSubject(0);
  total$ = this._total.asObservable();

  add(num: number) {
    this._total.next(this._total.value() + num);
  }

  subtract(num: number) {
    this._total.next(this._total.value() - num);
  }
}

您将如何测试 total$addsubtract 函数调用序列中发出正确的值,如下所示:

  service.add(10) // should emit 10;
  service.subtract(3) // should emit 7;
  service.add(20) // should emit 27;
  service.subtract(5) // should emit 22;
  ...

大理石测试对这样的事情有用吗?如果是这样,您将如何设置?我无法在网上找到一个明确的示例,说明如何测试服务上的可观察对象在给定该服务上的函数调用序列的情况下发出正确的值序列?

首先,我会尝试在没有弹珠图的情况下进行测试,只是为了确保我们了解异步执行的工作原理。

it('should test the Observable', () => {
    // create the instance of the service to use in the test
    const mathService = new MathService();
    // define the constant where we hold the notifications
    const result: number[] = [];
    const expected = [0, 0, 0, 1, 0, 2, 0];  // expected notifications

    // this is a sequence of adds
    const add$ = timer(0, 100).pipe(
        take(3),
        tap((i) => {
            console.log('add', i);
            mathService.add(i);
        }),
    );

    // this is a sequence of subtracts, which starts 50 ms after the adds
    const subtract$ = timer(50, 100).pipe(
        take(3),
        tap((i) => {
            console.log('sub', i);
            mathService.subtract(i);
        }),
    );

    // here we subscribe to total$ and we store any notification in the result array
    mathService.total$.subscribe({
        next: (s) => {
            result.push(s);
        },
    });

    // here we merge adds and subtracts and, at completion, we check which are the notifications
    // we have saved in the result array
    merge(add$, subtract$).subscribe({
        complete: () => {
            console.log('===>>>', result, expected);
        },
    });
});

一旦异步机制清楚了,那么我们可以看一下使用弹珠图的实现,比如这个

let testScheduler: TestScheduler;

beforeEach(() => {
    testScheduler = new TestScheduler(observableMatcher);
});

it.only('should test the Observable', () => {
    testScheduler.run(({ hot, expectObservable }) => {
        const mathService = new MathService();

        const add = hot('        --0-----1---2---------');
        const subtract = hot('   ----0-----1---2-------');
        const expected = '       --0-0---1-0-2-0-------';

        const _add = add.pipe(tap((i) => mathService.add(parseInt(i))));
        const _subtract = subtract.pipe(tap((i) => mathService.subtract(parseInt(i))));
        const result = merge(_add, _subtract).pipe(
            concatMap((val) => {
                console.log('val', val);
                return mathService.total$.pipe(map((v) => v.toString()));
            }),
        );

        expectObservable(result).toBe(expected);
    });
});

此实现遵循 rxJs 库中使用的一些测试示例。

observableMatchercan be seen here的实现。