Unit Test RxJS Observable.timer using typescript, karma and jasmine

Hi, I'm relatively new to Angular2, karma and jasmine. I am currently using Angular 2 RC4 Jasmine 2.4.x I have an Angular 2 service that periodically calls the http service, for example:

getDataFromDb() { return Observable.timer(0, 2000).flatMap(() => { return this.http.get(this.backendUrl) .map(this.extractData) .catch(this.handleError); }); } 

Now I want to test the functionality. For testing purposes, I just tested "http.get" on a separate function without Observable.timer by doing:

 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); }); }))); }); 

I obviously want to test the whole function using Observable.timer. I think it would be possible to use TestScheduler from the rxjs framework, but how can I say only to repeat the timer function x times? I can not find the documentation using it in the context of typescript.

Edit: I am using rxjs 5 beta 6

Edit: Added a working example for the final version of Angular 2.0.0:

 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
source share
2 answers

You need to introduce TestScheduler into the timer method inside each part:

 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); }); }); 

After that, you completely control the time with 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(); 

You need scheduler.flush() to run TestScheduler.

edit: therefore, if you only want to test X times, use the scheduleAbsolute functions as often (and with absolute absolute times in milliseconds) as you wish.

edit2: I added the missing scheduler start

edit3: I changed it, so it should work with RxJs5

+5
source

I had problems with the TestScheduler() approach because the schedule() arrow function was never executed, so I found another way.

The Observable.timer function simply returns an Observable, so I created it from scratch to give me full control.

First create a var for the observer:

 let timerObserver :Observer<any>; 

Now in beforeEach() create a spy and return Observable to it. Inside the observable, save your instance for the timer:

 beforeEach(() => { spyOn(Observable, 'timer').and.returnValue(Observable.create( (observer =>{ timerObserver = observer; }) )); }); 

In the test, just run Observable:

 it('Some Test',()=>{ // do stuff if needed // trigger the fake timer using the Observer reference timerObserver.next(''); timerObserver.complete(); // expect(somethingToHappenAfterTimerCompletes).toHaveBeenCalled(); }); 
0
source

Source: https://habr.com/ru/post/1267261/


All Articles