简体   繁体   English

angular2:如何测试具有可观察时间间隔的组件

[英]angular2: how to test a component which has an observable time interval

I have a slide-show component that has an Input array of slide objects and shows each one as long as it's been defined in slide.time of itself.我有一个幻灯片组件,它有一个幻灯片对象的输入数组,只要它在自己的slide.time中定义,就会显示每个幻灯片。 also there are two buttons that clicking them has to slide to the next item and reset the timer.还有两个按钮,单击它们必须滑动到下一个项目并重置计时器。 in order to make this work, I'm using Observables like this:为了完成这项工作,我正在使用这样的 Observables:

/**
 * a "SUBJECT" for pausing(restarting) the slider's auto-slide on user's click on left and right arrows
 * @type {Subject}
 */
private pauser = new Subject();

/**
 * the main observable for timer (before adding the pause/reset option)
 * @type {Observable<T>}
 */
private source = Observable
    .interval(1000)
    .timeInterval()
    .map(function (x) { /*return x.value + ':' + x.interval;*/ return x })
    .share();

/**
 * the final timer, which can be paused
 * @type {Observable<R>}
 */
private pausableSource = this.pauser.switchMap(paused => paused ? Observable.never() : this.source);

/**
 * the subscription to the timer which is assigned at OnInit hook , and destroyed at OnDestroy
 */
private subscription;

ngOnInit(){
    this.subscription = this.pausableSource.subscribe(() => {
        //doing changes to the template and changing between slides
    });
    this.pauser.next(false);
}

ngOnDestroy() {
    this.subscription.unsubscribe();
}

and it's working properly.它工作正常。 now to test this component, I'm giving it some data within a test-host component and want to check it's functionality like this:现在要测试这个组件,我在测试主机组件中给它一些数据,并想检查它的功能,如下所示:

it("should show the second (.slidingImg img) element after testHost.data[0].time seconds 
    have passed (which here, is 2 seconds)", () => {
    //testing
});

i tried many things that i found in docs or anywhere on the internet, but none of them work for me.我尝试了很多我在文档或互联网上的任何地方找到的东西,但它们都不适合我。 the problem is that I can't mock the passage of time in a way that the observable timer would perform next actions, and it's like no time has passed whatsoever.问题是我不能以可观察的计时器将执行下一个动作的方式模拟时间的流逝,就像没有时间过去一样。 two of the ways that haven't worked for me are these:其中两种对我不起作用的方法是:

it("should show the second (.slidingImg img) element after testHost.data[0].time seconds 
    have passed (which here, is 2 seconds)", fakeAsync(() => {
    fixture.detectChanges();
    tick(2500);
    flushMicrotasks();
    fixture.detectChanges();
    let secondSlidingImg = fixture.debugElement.queryAll(By.css('.slidingImg'))[1].query(By.css('img'));
    expect(secondSlidingImg).toBeTruthy();
    //error: expected null to be truthy
}));

i got this from angular2 docs.我从 angular2 文档中得到了这个。

and:和:

beforeEach(() => {
    fixture = TestBed.createComponent(TestHostComponent);

    testHost = fixture.componentInstance;

    scheduler = new TestScheduler((a, b) => expect(a).toEqual(b));

    const originalTimer = Observable.interval;
    spyOn(Observable, 'interval').and.callFake(function(initialDelay, dueTime) {
        return originalTimer.call(this, initialDelay, dueTime, scheduler);
    });
    // or:
    // const originalTimer = Observable.timer;
    // spyOn(Observable, 'timer').and.callFake(function(initialDelay, dueTime) {
    //     return originalTimer.call(this, initialDelay, dueTime, scheduler);
    // });
    scheduler.maxFrames = 5000;

});
it("should show the second (.slidingImg img) element after testHost.data[0].time seconds 
    have passed (which here, is 2 seconds)", async(() => {
    scheduler.schedule(() => {
        fixture.detectChanges();
        let secondSlidingImg = fixture.debugElement.queryAll(By.css('.slidingImg'))[1].query(By.css('img'));
        expect(secondSlidingImg).toBeTruthy();
        //error: expected null to be truthy
    }, 2500, null);
    scheduler.flush();
}));

i got this approach from this question .我从这个问题中得到了这种方法。

so I desperately need to know how exactly should I simulate time passage in my unit test so that the component's observable time interval would really trigger...所以我迫切需要知道我应该如何在单元测试中准确地模拟时间流逝,以便组件的可观察时间间隔真正触发......

versions:版本:

angular: "2.4.5"
"rxjs": "5.0.1"
"jasmine": "~2.4.1"
"karma": "^1.3.0"
"typescript": "~2.0.10"
"webpack": "2.2.1"    

fakeAsync doesn't work for some case with RxJs. fakeAsync不适用于某些情况下与RxJs工作。 You need to manually move internal timer in RxJs. 您需要在RxJs中手动移动内部计时器。 Something along those lines: 这些方面的东西:

import {async} from 'rxjs/internal/scheduler/async';
...

describe('faking internal RxJs scheduler', () => {
  let currentTime: number;

  beforeEach(() => {
    currentTime = 0;
    spyOn(async, 'now').and.callFake(() => currentTime);
  });

  it('testing RxJs delayed execution after 1000ms', fakeAsync(() => {
    // Do your stuff
    fixture.detectChanges();
    currentTime = 1000;
    tick(1000);
    discardPeriodicTasks();

    expect(...);
  }));
});

Best way I found for testing this is Marble Testing:我发现测试这个的最好方法是大理石测试:

Tutorial: https://medium.com/@bencabanes/marble-testing-observable-introduction-1f5ad39231c教程: https ://medium.com/@bencabanes/marble-testing-observable-introduction-1f5ad39231c

You can control emits by order and time, which seems to be exactly what you need.您可以按顺序和时间控制发射,这似乎正是您所需要的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM