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



我有以下服务:

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;
...

大理石测试对这样的东西有效吗?如果是的话,你会怎么安排?我在网上找不到一个明确的例子,说明如何在给定服务上的函数调用序列的情况下,测试服务上的可观察对象是否发出正确的值序列?

首先,我会尝试在没有大理石图的情况下进行测试,只是为了确保我们了解asyn执行是如何工作的。

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库中使用的一些测试示例。

observableMatcher的实现可以在这里看到。