我有一个幻灯片放映组件,它有一个幻灯片对象的输入数组,只要它在slide.time
中定义,就会显示每个对象。还有两个点击它们的按钮必须滑动到下一个项目并重置计时器。为了使这项工作,我正在使用这样的Observable:
/**
* 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();
}
它运作正常。现在要测试这个组件,我在测试主机组件中给它一些数据,并想检查它的功能如下:
it("should show the second (.slidingImg img) element after testHost.data[0].time seconds
have passed (which here, is 2 seconds)", () => {
//testing
});
我尝试了许多我在文档或互联网上找到的东西,但它们都不适合我。问题是我不能以可观察计时器执行下一步行动的方式来模仿时间的流逝,而且就像没有时间过去一样。对我来说没有用的两种方法是:
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
}));
我从angular2 docs得到了这个。
和:
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();
}));
我从this question得到了这个方法。
所以我迫切需要知道我应该如何在单元测试中模拟时间通过,以便组件的可观察时间间隔真正触发......
版本:
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
不适用于RxJs的某些情况。您需要在RxJs中手动移动内部计时器。这些方面的东西:
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(...);
}));
});