У меня есть следующий сервис:
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$
выдает правильные значения в последовательности вызовов функций add
и subtract
следующим образом:
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.info('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.info('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.info('===>>>', 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.info('val', val);
return mathService.total$.pipe(map((v) => v.toString()));
}),
);
expectObservable(result).toBe(expected);
});
});
Эта реализация следует некоторым примерам тестов, используемых в библиотеке rxjs.
Реализация observableMatcher
можно увидеть здесь.
Спасибо за это. Это было очень полезно. Однако я столкнулся с небольшой проблемой при воспроизведении вашей техники для мраморного теста. Все работает нормально, когда поток добавления и вычитания срабатывает на отдельных тиках, но когда я пытаюсь добавить параллельные события, тест начинает давать сбой с неожиданными значениями. Вы случайно не знаете, почему? Может ли это быть из-за того, как работает слияние?
Можете ли вы поделиться stackblitz с проблемой, с которой вы столкнулись?
Конечно, держи. Вы заметите, что ТРИ немного отличается от той, что была в моем вопросе, но достаточно похожа. stackblitz.com/edit/rxjs-d4qeys?file=index.ts
У вас своеобразная ситуация, по крайней мере, для шариков. Это версия вашего stackblitz, настроенный для работы с шариками, у которых есть уведомления, которые происходят в одном кадре. Вы можете прочитать мраморный синтаксическая документация для получения более подробной информации, но вкратце суть в том, что у вас есть process1
и process2
, которые уведомляют в одном и том же фрейме, и поэтому expected
должен указать, что он ожидает более одного уведомления в одном фрейме, используя круглые скобки.
Это действительно зависит от того, как вы хотите его протестировать, и если вы знаете, что в тесте (или макетах) вы можете гарантировать, что он всегда будет синхронным. Тогда вы можете подписаться на
total$
и собирать все, что он получает. Это также зависит от того, хотите ли вы использовать классические имитационные/шутливые вызовы ожидания или мраморное тестирование RxJS. Вы можете проверить, как Angular тестирует Observables внутри github.com/angular/angular/blob/master/packages/router/test/…