如何模拟 RxJs 6 计时器? [英] How do I mock RxJs 6 timer?
问题描述
我们最近从 Angular 5 更新到了 Angular 6,以及 RxJs 6.作为迁移的一部分,计时器的使用已从:
We've recently updated from Angular 5 to Angular 6, and with it RxJs 6. As part of the migration, the timer usage has changed from:
Observable.timer()
到
timer()
在我们的测试中有很多地方我们使用以下模式模拟计时器可观察对象.
There are a number of places in our tests where we mock timer observables with the following pattern.
let timerObserver: Observer<any>;
beforeEach(() => {
spyOn(Observable, 'timer').and.returnValue(Observable.create(
((observer: Observer<any>) => {
timerObserver = observer;
})
));
});
it(`should not have any notifications by default`, () => {
timerObserver.next('');
...
});
有人知道如何迁移这种模式吗?
Does anybody know how to migrate this pattern across?
我在这里创建了问题的简化说明:
I've created a simplified illustration of the problem here:
https://stackblitz.com/edit/angular-v6-testing-模板-nm7add
// Hello.Component
ngOnInit() {
const timer$ = timer(30);
timer$.subscribe(() => {
this.testMe = 'this has been changed';
});
}
// Hello.component.spec
it('should set testMe after a given timer', fakeAsync(() => {
tick(50);
expect(fixture.componentInstance.testMe).toBe('this has been changed');
}));
在这个例子中,我试图让计时器触发而不等待计时器解决.
In this example, I'm trying to get timer to trigger without waiting for the timer to resolve.
推荐答案
当您使用 fakeAsync
时,您可以依靠它对 setInterval
的修补来伪造 timer
observable 的实现.
As you are using fakeAsync
, you can rely upon its patching of setInterval
to fake the implementation of the timer
observable.
但是,您需要破坏 asyncScheduler
实例的 now
方法,因为它返回 Date.now()
.(严格来说,这对于 timer
可观察对象来说不是必需的,因为您已经使用了它,但它对于其他一些可观察对象很重要 - 例如由 delay
运算符).
However, you will need to clobber the asyncScheduler
instance's now
method, as it returns Date.now()
. (Strictly speaking, this isn't necessary for the timer
observable, as you've used it, but it will matter for some other observables - e.g. the observable returned by the delay
operator).
如果您使用 beforeEach
和 afterEach
来破坏 now
方法并配置一个保持追踪虚假时间:
You can get things to work pretty easily if you use beforeEach
and afterEach
to clobber the now
method and to configure a function that keeps track of the fake time:
import { fakeAsync, tick as _tick } from '@angular/core/testing';
import { asyncScheduler, of, timer } from 'rxjs';
import { delay } from 'rxjs/operators';
describe('fakeAsync and RxJS', () => {
let tick: (milliseconds: number) => void;
beforeEach(() => {
let fakeNow = 0;
tick = milliseconds => {
fakeNow += milliseconds;
_tick(milliseconds);
};
asyncScheduler.now = () => fakeNow;
});
it('should support timer with fakeAsync', fakeAsync(() => {
const source = timer(100);
let received: number | undefined;
source.subscribe(value => received = value);
tick(50);
expect(received).not.toBeDefined();
tick(50);
expect(received).toBe(0);
}));
it('should support delay with fakeAsync', fakeAsync(() => {
const source = of(0).pipe(delay(100));
let received: number | undefined;
source.subscribe(value => received = value);
tick(50);
expect(received).not.toBeDefined();
tick(50);
expect(received).toBe(0);
}));
afterEach(() => {
delete asyncScheduler.now;
});
});
<小时>
实际上,因为依靠 fakeAsync
来模拟基于时间的 observable 可能很有用,所以我在 rxjs-marbles
包.请参阅 fake-spec.ts代码>
例如用法.
Actually, because relying upon fakeAsync
to mock the time-based observable is likely to be useful, I've added a fakeSchedulers
function to my rxjs-marbles
package. See fake-spec.ts
for example usage.
实现与上面的代码片段基本相同 - 只是封装在一个函数中.
The implementation is basically the same as that in the above snippet - just wrapped up in a function.
自从写了这个答案并将 fakeSchedulers
添加到 rxjs-marbles
之后,我写了一篇关于 使用假时间进行测试.
Since writing this answer and adding fakeSchedulers
to rxjs-marbles
, I've written an article about Testing with Fake Time.
这篇关于如何模拟 RxJs 6 计时器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!