3

有时我们有函数,它使用延迟等异步运算符修改原始源。

让我们假设我们有一些非常简单的东西:

// Old syntax
function modify(source) {
   return source.delay(1000);
}

我正在使用弹珠来测试 RxJs,所以我的测试看起来像:

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 函数,因为我们无法直接访问它。而且效果很好。

但我们决定开始使用 RxJs 中的可管道操作符。有什么想法如何测试这个功能:

// New syntax
function modify(source) {
   return source.pipe(Rx.operators.delay(1000));
}

可能看起来像?

JsBin 演示

4

1 回答 1

0

UPD

rxjs 从 6.0.0 版本开始提供时间进度语法。所以你的测试可能看起来像:

it('generate the stream correctly', () => {
    testScheduler.run(helpers => {
        const { cold, expectObservable, expectSubscriptions } = helpers;
        const input = ' -a-b-c|';
        const expected = '-- 9ms a 9ms b 9ms (c|)';
        /*
        // Depending on your personal preferences you could also
        // use frame dashes to keep vertical aligment with the input
           const input = ' -a-b-c|';
           const expected = '------- 4ms a 9ms b 9ms (c|)';
        // or
           const expected = '-----------a 9ms b 9ms (c|)';

        */

        const result = cold(input).pipe(
            concatMap(d => of(d).pipe(
                delay(10)
            ))
        );

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

原始答案

终于找到了一些解决方法,不确定它是否适用于所有情况。

所以这里是我们修改函数的文件:

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);
    });
});
于 2018-10-05T20:18:40.740 回答