166

I have an angular service called requestNotificationChannel:

app.factory("requestNotificationChannel", function($rootScope) {

    var _DELETE_MESSAGE_ = "_DELETE_MESSAGE_";

    function deleteMessage(id, index) {
        $rootScope.$broadcast(_DELETE_MESSAGE_, { id: id, index: index });
    };

    return {
       deleteMessage: deleteMessage
    };

});

I am trying to unit test this service using jasmine:

"use strict";

describe("Request Notification Channel", function() {
    var requestNotificationChannel, rootScope, scope;

    beforeEach(function(_requestNotificationChannel_) {
        module("messageAppModule");

        inject(function($injector, _requestNotificationChannel_) {
            rootScope = $injector.get("$rootScope");
            scope = rootScope.$new();
            requestNotificationChannel = _requestNotificationChannel_;
        })

        spyOn(rootScope, '$broadcast');
    });


    it("should broadcast delete message notification", function(done) {

        requestNotificationChannel.deleteMessage(1, 4);
        expect(rootScope.$broadcast).toHaveBeenCalledWith("_DELETE_MESSAGE_", { id: 1, index: 4 });
        done();       
    });
});

I read about the Asynchronous Support in Jasmine, but as I am rather new to unit testing with javascript couldn't make it work.

I am receiving an error :

Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL

and my test is taking too long to execute (about 5s).

Can somebody help me providing working example of my code with some explanation?

SoEzPz
  • 14,958
  • 8
  • 61
  • 64
Mdb
  • 8,338
  • 22
  • 63
  • 98
  • 1
    Processing events is usually done in a digest cycle. Try adding scope.$apply() to your test instead of using Jasmine's async testing pattern – Eitan Peer Mar 24 '14 at 09:00
  • this does not worked. I added scope.$apply(); just after calling requestNotificationChannel.deleteMessage(1, 4) but I receivethe same error... – Mdb Mar 24 '14 at 09:07
  • I get the same error when [async tests take longer to run than `Jest` expects](https://github.com/facebook/jest/blob/master/docs/en/Troubleshooting.md#unresolved-promises) - very common while debugging and taking some time to inspect variables. – Dan Dascalescu Jun 11 '17 at 07:18
  • Try using lesser timeout instead. I got this error while using timeout = 5000. I replaced it with 2000 and it worked for me! – Marina Riaz May 16 '19 at 07:23
  • 2
    Leaving this here to help someone in my shoes. I had this error while running tests inside of a docker container. Tests would sometimes pass without any issues, but sometimes fail. I figured it was some kind of race condition but couldn't figure out why. I realised that I had an `afterEach` step that was clearing out the database (using the `deleteMany` method). Adding `jest.setTimeout(30000);` in the `beforeAll` method seems to have fixed this for me - I'm guessing since the database deletion is a network call (inside the condition), it was sometimes taking longer than 3 secs and throwing. – nkhil Jul 13 '20 at 17:34

20 Answers20

251

Having an argument in your it function (done in the code below) will cause Jasmine to attempt an async call.

//this block signature will trigger async behavior.
it("should work", function(done){
  //...
});

//this block signature will run synchronously
it("should work", function(){
  //...
});

It doesn't make a difference what the done argument is named, its existence is all that matters. I ran into this issue from too much copy/pasta.

The Jasmine Asynchronous Support docs note that argument (named done above) is a callback that can be called to let Jasmine know when an asynchronous function is complete. If you never call it, Jasmine will never know your test is done and will eventually timeout.

danronmoon
  • 3,814
  • 5
  • 34
  • 56
mastaBlasta
  • 5,700
  • 1
  • 24
  • 26
  • 3
    The same is true for args in describe (in angular, you need to call inject in describe to do that) – Narretz Oct 20 '14 at 09:57
  • @MartinBliss It is documented, I've justed suggested an edit to refer to the documentation: http://stackoverflow.com/suggested-edits/2434606 – Vincent Apr 05 '16 at 06:28
  • 59
    Note to random Googlers coming across this question in the future: if you are using Protractor and run into this issue, this answer isn't what you're looking for - Protractor calls the callback by itself. – Vincent Apr 05 '16 at 06:31
  • It fixed my problem and it was caused by the same cuplrit "copy/pasta" – shaikh Sep 06 '16 at 10:08
  • `I ran into this issue from too much copy/pasta` Classic line and so true :D – atconway Oct 09 '18 at 02:36
  • 3
    @Vincent what's the Protractor users issue then if this error occurs? – Bruno Bieri Nov 22 '18 at 13:17
  • @BrunoBieri Well, it's been 4.5 years since that comment, so I'm not really sure... I'd guess Angular.js's digest cycle not completing or something? I'm sorry, can't really help there today... – Vincent Nov 23 '18 at 14:11
  • In edition to this: doing `() =>{...}` & `_ => {...}` are NOT the same thing. Jasmine will parse the `_` as an argument and lead you to this error. – Callat Feb 07 '20 at 06:19
81

Even for async tests, there is a timeout that goes off in this cases, You can work around this error by increasing the value for the limit timeout to evaluate an async Jasmine callback

describe('Helper', function () {
    var originalTimeout;

    beforeEach(function() {
        originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
        jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000000;
    });

    afterEach(function() {
      jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
    });

    it('Template advance', function(doneFn) {
        $.ajax({
            url: 'public/your-end-point.mock.json',
            dataType: 'json',
            success: function (data, response) {
                // Here your expected using data
                expect(1).toBe(1)
                doneFn();
            },
            error: function (data, response) {
                // Here your expected using data
                expect(1).toBe(1)
                doneFn();
            }
        });
    });
});

Source: http://jasmine.github.io/2.0/introduction.html#section-42

gsalgadotoledo
  • 2,666
  • 1
  • 25
  • 22
  • 1
    This seems like not "the right way" to do it, but after adding a couple extra zeros for my Selenium test to run, this was a needed hack. – emery Nov 01 '17 at 20:11
  • The original jasmine.DEFAULT_TIMEOUT_INTERVAL is 60000 ms. So this example will actually make it six times shorter. – Waltari Oct 17 '18 at 12:30
  • You're right I just put a random number in this example, thanks :) – gsalgadotoledo Oct 17 '18 at 21:41
20

This error can also be caused by leaving out inject when initializing a service/factory or whatever. For example, it can be thrown by doing this:

var service;
beforeEach(function(_TestService_) {
    service = _TestService_;
});

To fix it just wrap the function with inject to properly retrieve the service:

var service;
beforeEach(inject(function(_TestService_) {
    service = _TestService_;
}));
Bruno Bieri
  • 9,724
  • 11
  • 63
  • 92
Katana24
  • 8,706
  • 19
  • 76
  • 118
19
import { fakeAsync, ComponentFixture, TestBed } from '@angular/core/testing';

use fakeAsync

beforeEach(fakeAsync (() => {

//your code

}));



describe('Intilalize', () => {
        it('should have a defined component', fakeAsync(() => {
            createComponent();
            expect(_AddComponent.ngOnInit).toBeDefined();
        }));
    });
Lijo
  • 6,498
  • 5
  • 49
  • 60
10

You can use karma-jasmine plugin to set the default time out interval globally.

Add this config in karma.conf.js

module.exports = function(config) {
  config.set({
    client: {
      jasmine: {
        timeoutInterval: 10000
      }
    }
  })
}
code.rookie
  • 346
  • 2
  • 6
5

This error started out of the blue for me, on a test that had always worked. I couldn't find any suggestions that helped until I noticed my Macbook was running sluggishly. I noticed the CPU was pegged by another process, which I killed. The Jasmine async error disappeared and my tests are fine once again.

Don't ask me why, I don't know. But in my circumstance it seemed to be a lack of system resources at fault.

Eric Soyke
  • 1,073
  • 1
  • 15
  • 30
  • 6
    Probably, when your CPU was free, the task finished before the default timeout. When the CPU was busy, the task you were testing took too long to complete. – Shane Mar 23 '17 at 20:27
5

This is more of an observation than an answer, but it may help others who were as frustrated as I was.

I kept getting this error from two tests in my suite. I thought I had simply broken the tests with the refactoring I was doing, so after backing out changes didn't work, I reverted to earlier code, twice (two revisions back) thinking it'd get rid of the error. Doing so changed nothing. I chased my tail all day yesterday, and part of this morning without resolving the issue.

I got frustrated and checked out the code onto a laptop this morning. Ran the entire test suite (about 180 tests), no errors. So the errors were never in the code or tests. Went back to my dev box and rebooted it to clear anything in memory that might have been causing the issue. No change, same errors on the same two tests. So I deleted the directory from my machine, and checked it back out. Voila! No errors.

No idea what caused it, or how to fix it, but deleting the working directory and checking it back out fixed whatever it was.

Hope this helps someone.

delliottg
  • 3,950
  • 3
  • 38
  • 52
  • 1
    Thank you man, i was going crazy about this. I rebooted my PC and that was it – yngrdyn Apr 09 '19 at 08:30
  • In my case, I just re-ran the command and it resolved this issue. I had hot reload for the unit-tests and everytime it was failing. I had to stop and run command again. – jignesh Jul 17 '19 at 08:42
3

You also get this error when expecting something in the beforeAll function!

describe('...', function () {

    beforeAll(function () {
        ...

        expect(element(by.css('[id="title"]')).isDisplayed()).toBe(true);
    });

    it('should successfully ...', function () {

    }
}
Tom Van Rossom
  • 1,440
  • 10
  • 18
3

Don't use done, just leave the function call empty.

Alper Kucukkomurler
  • 1,706
  • 2
  • 13
  • 19
  • 1
    Please correct me if I am wrong but as I have understood it just makes the test suite finish before the very test and and the error message is dumped instead. Which means that any assert that fails won't break the test as the test suite is finished before the assert is run. It might also mean (I have seen similar behaviour) that another test shows the error this test created. Finally it means that everything looks ok to start with but as the number of tests grow the issue will pop up intermittently. – LosManos Jun 25 '20 at 19:33
3

It looks like the test is waiting for some callback that never comes. It's likely because the test is not executed with asynchronous behavior.

First, see if just using fakeAsync in your "it" scenario:

it('should do something', fakeAsync(() => {

You can also use flush() to wait for the microTask queue to finish or tick() to wait a specified amount of time.

Shapeshifter
  • 510
  • 6
  • 11
2

Works after removing the scope reference and the function arguments:

"use strict";

describe("Request Notification Channel", function() {
    var requestNotificationChannel, rootScope;

    beforeEach(function() {
        module("messageAppModule");

        inject(function($injector, _requestNotificationChannel_) {
            rootScope = $injector.get("$rootScope");
            requestNotificationChannel = _requestNotificationChannel_;
        })
        spyOn(rootScope, "$broadcast");
    });


    it("should broadcast delete message notification with provided params", function() {
        requestNotificationChannel.deleteMessage(1, 4);
        expect(rootScope.$broadcast).toHaveBeenCalledWith("_DELETE_MESSAGE_", { id: 1, index: 4} );
    });
});
Bruno Bieri
  • 9,724
  • 11
  • 63
  • 92
Paul Sweatte
  • 24,148
  • 7
  • 127
  • 265
2

In my case, this error was caused by improper use of "fixture.detectChanges()" It seems this method is an event listener (async) which will only respond a callback when changes are detected. If no changes are detected it will not invoke the callback, resulting in a timeout error. Hope this helps :)

lesyk
  • 3,979
  • 3
  • 25
  • 39
A. Figueroa
  • 215
  • 1
  • 8
1

What I did was: Added/Updated the following code:

framework: 'jasmine',
jasmineNodeOpts: 
{
    // Jasmine default timeout
    defaultTimeoutInterval: 60000,
    expectationResultHandler(passed, assertion) 
    {
      // do something
    },
}
4b0
  • 21,981
  • 30
  • 95
  • 142
Zeeshan
  • 19
  • 1
  • 4
    Please explain why this code works, rather than just posting it without an explanation. – Kobe Jul 02 '19 at 12:38
  • So basically when you execute a test and it takes longer than expected, it gets failed because the default time out was met and script didn't move forward in execution. This could happen due to some conditions not met (For example visibility, page loading). Now if your default time out is like 1000ms >> scripts would fail often because it's just one second and there could be multiple factors adding to the failure of your script. However, increasing your timeout interval can make browser/driver wait longer for conditions to met. – Zeeshan Jul 09 '19 at 12:12
  • 2
    Okay, now word that into your post; you should try to avoid just answering with code without explanation :) – Kobe Jul 09 '19 at 12:23
0

As noted by @mastablasta, but also to add that if you call the 'done' argument or rather name it completed you just call the callback completed() in your test when it's done.

// this block signature will trigger async behavior.
it("should work", function(done){
  // do stuff and then call done...
  done();
});

// this block signature will run synchronously
it("should work", function(){
  //...
});
SoEzPz
  • 14,958
  • 8
  • 61
  • 64
0

jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;

Keeping this in the block solved my issue.

it('', () => {
 jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
});
Sai Prasad
  • 131
  • 9
0

Instead of

beforeEach(() => {..

use

beforeEach(fakeAsync(() => {..
Pochmurnik
  • 780
  • 6
  • 18
  • 35
Meghnath Das
  • 145
  • 1
  • 6
0

In my case, a timeout was cause because of a failed injection of a service with providedIn: 'root'. It's not clear why injection failed, nor why there was no early error if there is apparently no instance of provider available.

I was able to work around it by manually providing a value:

TestBed.configureTestingModule({
  declarations: [
    // ...
  ],
  imports: [
    // ...
  ],
  providers: [
    // ...
    { provide: MyService, useValue: { /* ... */ } },
  ]
}).compileComponents();
Cedric Reichenbach
  • 8,970
  • 6
  • 54
  • 89
0

I have caught the same error because I used the setTimeout function in the component. Example:

  ngOnInit(): void {
    this.changeState();
  }
  
  private changeState(): void {
    setTimeout(() => this.state = StateEnum.IN_PROGRESS, 10000);
  }

When I changed the timeout from 10000ms to 0 or less than 5000ms (DEFAULT_TIMEOUT_INTERVAL), all tests were passed.

Denis Golubev
  • 96
  • 1
  • 6
0

In my case, I was not returning the value from the spy method, hence facing error,

        mainMethod(args): Observable<something>{
            return nestedMethod().pipe();
        }

Your Test should like below,

        it('your test case', (done: DoneFn) => {
         const testData = {}; // Your data    
         spyOn(service, 'nestedMethod').and.returnValue(of(testData));
         const obxValue = service.mainMethod('your args');

         obxValue.pipe(first()).subscribe((data) => {
            expect(data).not.toBeUndefined();
            done();
         });
       });
VKD
  • 16
  • 2
-2

If you have an argument (done) in the it function try to remove it as well it's call within the function itself:

it("should broadcast delete message notification", function(/*done -> YOU SHOULD REMOVE IT */) {

    requestNotificationChannel.deleteMessage(1, 4);
    expect(rootScope.$broadcast).toHaveBeenCalledWith("_DELETE_MESSAGE_", { id: 1, index: 4 });
    // done(); -> YOU SHOULD REMOVE IT        
});
Bruno Bieri
  • 9,724
  • 11
  • 63
  • 92
tiagor87
  • 15
  • 1
  • 5