正在测试使用setInterval或setTimeout的Angular2组件
Testing Angular2 components that use setInterval or setTimeout
我有一个非常典型、简单的ng2组件,它调用一个服务来获取一些数据(转盘项目)。它还使用setInterval每隔n秒在UI中自动切换转盘幻灯片。它工作得很好,但当运行Jasmine测试时,我得到了错误:"不能在异步测试区域内使用setInterval"。
我尝试将setInterval调用包装在这个.zone.runOutsideAngular(()=>{…})中,但错误仍然存在。我本以为将测试更改为在fakeAsync区域中运行会解决问题,但后来我收到一个错误,说不允许从fakeAsync-测试区域内进行XHR调用(这确实有道理)。
如何在仍然能够测试组件的同时使用服务和间隔进行的XHR调用?我使用的是ng2-rc4,由angular cli生成的项目。非常感谢。
我的组件代码:
constructor(private carouselService: CarouselService) {
}
ngOnInit() {
this.carouselService.getItems().subscribe(items => {
this.items = items;
});
this.interval = setInterval(() => {
this.forward();
}, this.intervalMs);
}
Jasmine规格:
it('should display carousel items', async(() => {
testComponentBuilder
.overrideProviders(CarouselComponent, [provide(CarouselService, { useClass: CarouselServiceMock })])
.createAsync(CarouselComponent).then((fixture: ComponentFixture<CarouselComponent>) => {
fixture.detectChanges();
let compiled = fixture.debugElement.nativeElement;
// some expectations here;
});
}));
Clean代码是可测试的代码。CCD_ 1有时很难测试,因为时间从来都不是完美的。您应该将setTimeout
抽象为一个服务,以便为测试进行模拟。在mock中,您可以使用控件来处理间隔的每个刻度。例如
class IntervalService {
interval;
setInterval(time: number, callback: () => void) {
this.interval = setInterval(callback, time);
}
clearInterval() {
clearInterval(this.interval);
}
}
class MockIntervalService {
callback;
clearInterval = jasmine.createSpy('clearInterval');
setInterval(time: number, callback: () => void): any {
this.callback = callback;
return null;
}
tick() {
this.callback();
}
}
有了MockIntervalService
,您现在可以控制每个刻度,这在测试过程中更容易推理。还有一个间谍可以检查组件被销毁时是否调用了clearInterval
方法。
对于您的CarouselService
,由于它也是异步的,请参阅这篇文章以获得一个好的解决方案。
以下是使用前面提到的服务的完整示例(使用RC 6)。
import { Component, OnInit, OnDestroy } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TestBed } from '@angular/core/testing';
class IntervalService {
interval;
setInterval(time: number, callback: () => void) {
this.interval = setInterval(callback, time);
}
clearInterval() {
clearInterval(this.interval);
}
}
class MockIntervalService {
callback;
clearInterval = jasmine.createSpy('clearInterval');
setInterval(time: number, callback: () => void): any {
this.callback = callback;
return null;
}
tick() {
this.callback();
}
}
@Component({
template: '<span *ngIf="value">{{ value }}</span>',
})
class TestComponent implements OnInit, OnDestroy {
value;
constructor(private _intervalService: IntervalService) {}
ngOnInit() {
let counter = 0;
this._intervalService.setInterval(1000, () => {
this.value = ++counter;
});
}
ngOnDestroy() {
this._intervalService.clearInterval();
}
}
describe('component: TestComponent', () => {
let mockIntervalService: MockIntervalService;
beforeEach(() => {
mockIntervalService = new MockIntervalService();
TestBed.configureTestingModule({
imports: [ CommonModule ],
declarations: [ TestComponent ],
providers: [
{ provide: IntervalService, useValue: mockIntervalService }
]
});
});
it('should set the value on each tick', () => {
let fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
let el = fixture.debugElement.nativeElement;
expect(el.querySelector('span')).toBeNull();
mockIntervalService.tick();
fixture.detectChanges();
expect(el.innerHTML).toContain('1');
mockIntervalService.tick();
fixture.detectChanges();
expect(el.innerHTML).toContain('2');
});
it('should clear the interval when component is destroyed', () => {
let fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
fixture.destroy();
expect(mockIntervalService.clearInterval).toHaveBeenCalled();
});
});
我也遇到了同样的问题:特别是,当第三方服务从测试中调用setInterval()
时,出现了这个错误:
错误:无法在异步区域测试中使用setInterval。
您可以模拟调用,但这并不总是可取的,因为您可能实际上想要测试与另一个模块的交互。
在我的案例中,我只使用Jasmine的(>=2.0)异步支持而不是Angulars的async()
:就解决了这个问题
it('Test MyAsyncService', (done) => {
var myService = new MyAsyncService()
myService.find().timeout(1000).toPromise() // find() returns Observable.
.then((m: any) => { console.warn(m); done(); })
.catch((e: any) => { console.warn('An error occured: ' + e); done(); })
console.warn("End of test.")
});
使用Observable怎么样?https://github.com/angular/angular/issues/6539要测试它们,您应该使用.toPromise()方法
- 如何在Angular2中使用jQuery插件
- 不能从angular2中的子组件指定父组件中的数组
- setInteval vs setTimeout
- 如何在chrome扩展中存储数据/结果,以及如何使用setTimeout使其只被调用一次
- setTimeout可以与闭包内的函数一起使用吗
- Javascript setTimeout for an array
- 刷新后,setTimeout将工作或不工作
- 继续使用javascript中的setTimeout
- 如何为生产构建angular2应用程序
- setTimeout函数能否在其前面的代码执行之前激发
- jQuery setTimeOut: in for-loop
- SetTimeout and clearTimeout in Javascript
- 在setTimeout中调用相同的函数
- setTimeout不能太长
- 使用javascript协调angular2组件
- Javascript-SetTimeout导致浏览器冻结
- setTimeout and V8
- Angular2将组件方法导入到另一个组件中
- Angular2,视图在settimeout变量更改后未更新
- 正在测试使用setInterval或setTimeout的Angular2组件