Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Test Uncaught Error: macroTask 'setInterval': can not transition to 'running', expecting state 'scheduled', was 'notScheduled'

I am using Angular version "4.2.2", Angular Cli version "1.1.1" and zone.js 0.8.12. Some of my unit tests that used to work either in Chrome and PhanthomJS are now failing with the following error:

ERROR Uncaught Error: macroTask 'setInterval': can not transition to 'running', expecting state 'scheduled', was 'notScheduled'.

Anyone has seen these errors before and know how to fix them?

like image 435
Sil Avatar asked Jun 13 '17 11:06

Sil


1 Answers

Ok, so after a bit of research, I've found that most people (even the Angular people), say themselves that using async or fakeAsync with code that involves the intervalTimer (common with Observable methods) simply cannot be done.

Where I found this was at this link

For example, I was testing an observable method with the fakeAsync and tick technique spelled out in the Angular2 docs and was running into this error. (see test below)

it('#findCaseMasks should show error if error is returned from service', fakeAsync(() => {
        component.searchCriteria = new CaseMaskModel(1, 1);
        caseMaskServiceSpy = spyOn(service, 'findCaseMasks').and.returnValue(Observable.throw('error occured', Scheduler.async));
        component.findCaseMasks();
        expect(component.error).toBeNull;
        tick();
        fixture.detectChanges();
        expect(component.error).toEqual('Something went wrong. Please contact helpdesk if the issue persists.');
    }));

The test above was changed to what you see below and the test began working fine.

it('#findCaseMasks should show error if error is returned from service', (done) => {
        component.searchCriteria = new CaseMaskModel(1, 1);
        caseMaskServiceSpy = spyOn(service, 'findCaseMasks').and.returnValue(Observable.throw('error occured', Scheduler.async));
        component.findCaseMasks();
        expect(component.error).toBeNull;
        caseMaskServiceSpy.calls.mostRecent().returnValue.subscribe(
            () => { //success
                //put code here if testing for success
            },
            err => { //error
                //put code here if testing for error

                fixture.detectChanges();
                expect(component.error).toEqual('Something went wrong. Please contact helpdesk if the issue persists.');
                done();
            });
    });

So basically you have to spy on the service that was injected into the component with `jasmine.Spy' and then subscribe to its most recent calls. Then you have familiar subscription syntax in order to check for errors and to check for success.

For reference, the steps to spy on the injected service are as follows:

  1. Add a variable at the top of the describe method that is of the same type of the service that you're trying to spy, ie. let casemaskService: CaseMaskService;
  2. Create a variable at the top of the describe method that is of the type jasmine.Spy, ie let casemaskServiceSpy: jasmine.Spy;
  3. Set the service variable equal to the fixture's injected service of the same type, ie casemaskService = fixture.debugElement.injector.get(CaseMaskService);. (I usually do this in the synchronous beforeEach method)
  4. At any point during any of your specs, just set the spy variable equal to a spyOn instance and tell it what to return, ie caseMaskSpy = spyOn(casemaskService, 'updateCaseMask').and.returnValue(Observable.of(${mockCaseMask.caseMaskingId} was updated!, Scheduler.async));

At that point you can use the code I've pasted above and should be good to go.

like image 101
OysterMaker Avatar answered Oct 25 '22 02:10

OysterMaker