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

天大地大妈咪最大 提交于 2019-12-07 04:00:35

问题


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

/**
 * 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.

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"    

回答1:


fakeAsync doesn't work for some case with RxJs. You need to manually move internal timer in 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(...);
  }));
});


来源:https://stackoverflow.com/questions/43674151/angular2-how-to-test-a-component-which-has-an-observable-time-interval

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!