Модульный Тест RxJS Наблюдаем.таймер с использованием typescript, karma и jasmine
Привет я относительно новым для Angular2, кармы и Жасмин. В настоящее время я использую Angular 2 RC4 Jasmine 2.4.икс У меня есть служба Angular 2, которая периодически вызывает службу http следующим образом:
getDataFromDb() { return Observable.timer(0, 2000).flatMap(() => {
return this.http.get(this.backendUrl)
.map(this.extractData)
.catch(this.handleError);
});
}
Теперь я хочу проверить функциональность. Для целей тестирования я только что протестировал " http.получить " на отдельную функцию без наблюдаемого.таймер, выполнив:
const mockHttpProvider = {
deps: [MockBackend, BaseRequestOptions],
useFactory: (backend: MockBackend, defaultOptions: BaseRequestOptions) => {
return new Http(backend, defaultOptions);
}
}
describe('data.service test suite', () => {
var dataFromDbExpected: any;
beforeEachProviders(() => {
return [
DataService,
MockBackend,
BaseRequestOptions,
provide(Http, mockHttpProvider),
];
});
it('http call to obtain data',
inject(
[DataService, MockBackend],
fakeAsync((service: DataService, backend: MockBackend) => {
backend.connections.subscribe((connection: MockConnection) => {
dataFromDbExpected = 'myData';
let mockResponseBody: any = 'myData';
let response = new ResponseOptions({ body: mockResponseBody });
connection.mockRespond(new Response(response));
});
const parsedData$ = service.getDataFromDb()
.subscribe(response => {
console.log(response);
expect(response).toEqual(dataFromDbExpected);
});
})));
});
Я, очевидно, хочу проверить всю функцию с наблюдаемым.таймер. Я думаю, что можно было бы использовать TestScheduler из фреймворка rxjs, но как я могу сказать, чтобы только повторить функцию таймера для x раз? Я не смог найти никакой документации, использующей его в контексте машинописи.
Edit: я использую rxjs 5 beta 6
Edit: добавлен рабочий пример для Angular 2.0.0 final release:
describe('when getData', () => {
let backend: MockBackend;
let service: MyService;
let fakeData: MyData[];
let response: Response;
let scheduler: TestScheduler;
beforeEach(inject([Http, XHRBackend], (http: Http, be: MockBackend) => {
backend = be;
service = new MyService(http);
fakeData = [{myfake: 'data'}];
let options = new ResponseOptions({ status: 200, body: fakeData });
response = new Response(options);
scheduler = new TestScheduler((a, b) => expect(a).toEqual(b));
const originalTimer = Observable.timer;
spyOn(Observable, 'timer').and.callFake(function (initialDelay, dueTime) {
return originalTimer.call(this, initialDelay, dueTime, scheduler);
});
}));
it('Should do myTest', async(inject([], () => {
backend.connections.subscribe((c: MockConnection) => c.mockRespond(response));
scheduler.schedule(() => {
service.getMyData().subscribe(
myData => {
expect(myData.length).toBe(3,
'should have expected ...');
});
}, 2000, null);
scheduler.flush();
})));
});
4 ответа:
Вам нужно ввести TestScheduler в метод timer внутри части beforeEach:
beforeEach(function() { this.scheduler = new TestScheduler(); const originalTimer = Observable.timer; spyOn(Observable, 'timer').and.callFake(function(initialDelay, dueTime) { return originalTimer.call(this, initialDelay, dueTime, this.scheduler); }); });
После этого вы полностью контролируете время с помощью
scheduleAbsolute
:this.scheduler.schedule(() => { // should have been called once // You can put your test code here }, 1999, null); this.scheduler.schedule(() => { // should have been called twice // You can put your test code here }, 2000, null); this.scheduler.schedule(() => { // should have been called three times // You can put your test code here }, 4000, null); this.scheduler.flush();
Нужно
scheduler.flush()
чтобы начать TestScheduler.Edit: поэтому, если вы хотите проверить его только X раз, используйте функции scheduleAbsolute так часто (и с правильным абсолютным временем в миллисекундах), как вы хотите.
Edit2: я добавил недостающий запуск планировщика
Edit3: я изменил его так должно быть работа с RxJs5
У меня были проблемы с подходом
TestScheduler()
, потому что функция стрелкиschedule()
никогда не выполнялась, поэтому я нашел другой путь.Функция
Observable.timer
просто возвращает наблюдаемое, поэтому я создал его с нуля, чтобы дать мне полный контроль.Сначала создайте var для наблюдателя:
let timerObserver: Observer<any>;
Теперь в
beforeEach()
Создайте шпиона и пусть он вернет наблюдаемое. Внутри наблюдаемого сохраните экземпляр в таймер:beforeEach(() => { spyOn(Observable, 'timer').and.returnValue(Observable.create( (observer => { timerObserver = observer; }) )); });
В тесте просто запустите Наблюдаемый:
it('Some Test',()=>{ // do stuff if needed // trigger the fake timer using the Observer reference timerObserver.next(''); timerObserver.complete(); expect(somethingToHappenAfterTimerCompletes).toHaveBeenCalled(); });
Я тоже некоторое время боролся с этим. Поскольку, по-видимому, многое изменилось в рамках с тех пор, как был задан этот вопрос, я подумал, что, возможно, кому-то поможет мое решение. Мой проект использует rxjs 5, jasmine 2.8 и angular 5.
В моем компоненте таймер использовался для вызова функции http-get в службе каждую минуту. Моя проблема заключалась в том, что при использовании зоны fakeAsync функция get (stubbed) никогда не вызывалась, и я получил сообщение об ошибке: "Ошибка: 1 периодический таймер(ы) все еще в очередь.".
Ошибка появляется, потому что таймер продолжает работать и не останавливается в конце теста. Это можно решить, добавив "discardPeriodicTasks ();" в конец теста, что приводит к остановке таймера. Tick (); может использоваться для подделки времени до следующего вызова. Я использовал шпиона на моей get-функции в моей службе, чтобы увидеть, если это работает:
it( 'should call getTickets from service every .. ms as defined in refreshTime', fakeAsync(() => { fixture.detectChanges(); tick(); expect(getTicketsSpy).toHaveBeenCalledTimes(1); // let 2 * refreshtime pass tick(2 * component.refreshTime); expect(getTicketsSpy).toHaveBeenCalledTimes(3); discardPeriodicTasks(); }) );
RefreshTime-это параметр, который я использовал в таймере. Я надеюсь, что это помешает кому-то потратить половину Дэй пытается разобраться в этом.
Вы можете довольно легко проверить наблюдаемые таймеры с помощью
fakeAsync()
. Вот компонент, который отображает таймер обратного отсчета (используя длительность momentJS):Тайм-аут.деталь.ts
@Component({ selector: 'app-timeout-modal', templateUrl: './timeout-modal.component.html' }) export class TimeoutModalComponent implements OnInit { countdownTimer: Observable<number>; countdownSubscription: Subscription; durationLeft = moment.duration(60000); // millis - 60 seconds ngOnInit() { this.countdownTimer = Observable.timer(0, 1000); this.countdownSubscription = this.countdownTimer .do(() => this.durationLeft.subtract(1, 's')) .takeWhile(seconds => this.durationLeft.asSeconds() >= 0) .subscribe(() => { if (this.durationLeft.asSeconds() === 0) { this.logout(); } }); } }
Тайм-аут.деталь.спекуляция.ts
beforeEach(async(() => { ... })); beforeEach(() => { fixture = TestBed.createComponent(TimeoutModalComponent); component = fixture.componentInstance; }); it('should show a count down', fakeAsync(() => { fixture.detectChanges(); expect(component.durationLeft.asSeconds()).toEqual(60); tick(1000); fixture.detectChanges(); expect(component.durationLeft.asSeconds()).toEqual(59); component.countdownSubscription.unsubscribe(); }));