Тестирование асинхронных конвейерных операторов в RxJS
Иногда у нас есть функции, которые изменяют исходный источник, используя асинхронные операторы, такие как задержка.
Предположим, у нас есть что-то действительно простое:
// Old syntax
function modify(source) {
return source.delay(1000);
}
Я использую шарики для тестирования RxJ, поэтому мой тест выглядит так:
it("mock chain style call (modify function)", function() {
var values = {
a: "test",
x: "test"
};
var source = hot( "-a", values);
var delayTime = time( "--|");
var result = cold( "---x", values);
var originalDelay = Rx.Observable.prototype.delay;
spyOn(Rx.Observable.prototype, "delay").and.callFake(function () {
return originalDelay.call(this, delayTime, jm.getTestScheduler());
});
expect(modify(source)).toBeObservable(result);
});
Это почти то же самое, что используется в библиотеке rxjs для тестирования: https://github.com/ReactiveX/rxjs/blob/master/spec/operators/delay-spec.ts
Но мы должны пропатчить функцию Observable.delay, потому что у нас нет прямого доступа к ней. И это хорошо работает.
Но мы решили начать использовать конвейерные операторы из RxJ. Есть ли идеи, как проверить эту функцию:
// New syntax
function modify(source) {
return source.pipe(Rx.operators.delay(1000));
}
может выглядеть?
1 ответ
Наконец-то нашел обходной путь, не уверен, что это применимо для всех случаев.
Итак, вот файл с нашими функциями модификации:
import { Observable } from "rxjs";
import { delay } from "rxjs/operators";
export function modify<T>(source: Observable<T>): Observable<T> {
return source.pipe(delay(1000));
}
Основная идея здесь состоит в том, чтобы импортировать все операторы как объект, и это шпионит за методом этих объектов:
import * as operators from "rxjs/operators";
...
spyOn(operators, "delay")
Проблема здесь в том, что я получил ошибку:
Error: <spyOn> : delay is not declared writable or has no setter
Чтобы избежать этой ошибки, я просто изменил дескриптор свойства в объекте:
/**
* Changes property descriptor to make possible to use spyOn function for imported
* modules.
*/
function spyOnOperator(obj: any, prop: string): any {
const oldProp: Function = obj[prop];
Object.defineProperty(obj, prop, {
configurable: true,
enumerable: true,
value: oldProp,
writable: true
});
return spyOn(obj, prop);
}
Итак, мой файл спецификаций теперь выглядит так:
import { cold, getTestScheduler, hot, time } from "jasmine-marbles";
import * as operators from "rxjs/operators";
import { modify } from "./modify";
describe("Test delay: ", () => {
it("mock chain style call (modify function)", () => {
const originalDelay: Function = operators.delay;
const values: any = {
a: "test",
x: "test"
};
const source: TestHotObservable = hot("-a", values);
const delayTime: number = time("--|");
const result: TestColdObservable = cold("---x", values);
spyOnOperator(operators, "delay").and.callFake(() => {
return originalDelay.call(this, delayTime, getTestScheduler());
});
expect(modify(source)).toBeObservable(result);
});
});