问题描述
我们最近从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- template-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
可观察的实现.
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
来模拟基于时间的可观察对象可能很有用,所以我在 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计时器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!