Ответ 1
Чистый код - это тестируемый код. setInterval
иногда трудно проверить, потому что время никогда не бывает идеальным. Вы должны абстрагировать setTimeout
в службу, которую вы можете высмеять для теста. В макете вы можете иметь элементы управления для каждого тика интервала. Например
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();
});
});