Mdb
Mdb

Reputation: 8556

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

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?

Upvotes: 173

Views: 304429

Answers (21)

Shapeshifter
Shapeshifter

Reputation: 560

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 it passes 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.

Upvotes: 6

ashishn
ashishn

Reputation: 438

for someone facing this kind, I was writing a test for the first time and had this as test statement and test failing with timeout error.

  fit('log in with Facebook ', async done => {

Fix i found was adding done() as last statement of test.

fit('log in with Facebook ', async done => {
.....
.....

done();
})

Upvotes: 0

VKD
VKD

Reputation: 16

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

Upvotes: 0

Denis Golubev
Denis Golubev

Reputation: 101

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.

Upvotes: 0

Cedric Reichenbach
Cedric Reichenbach

Reputation: 9319

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

Upvotes: 0

Anastasiia Semionova
Anastasiia Semionova

Reputation: 47

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

Upvotes: 3

gsalgadotoledo
gsalgadotoledo

Reputation: 2714

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

Upvotes: 82

mastaBlasta
mastaBlasta

Reputation: 5860

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.

Upvotes: 258

Meghnath Das
Meghnath Das

Reputation: 155

Instead of

beforeEach(() => {..

use

beforeEach(fakeAsync(() => {..

Upvotes: 0

Zeeshan
Zeeshan

Reputation: 19

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

framework: 'jasmine',
jasmineNodeOpts: 
{
    // Jasmine default timeout
    defaultTimeoutInterval: 60000,
    expectationResultHandler(passed, assertion) 
    {
      // do something
    },
}

Upvotes: 1

code.rookie
code.rookie

Reputation: 356

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

Upvotes: 10

Katana24
Katana24

Reputation: 8959

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

Upvotes: 20

Paul Sweatte
Paul Sweatte

Reputation: 24627

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

Upvotes: 2

tiagor87
tiagor87

Reputation: 15

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

Upvotes: -2

Sai Prasad
Sai Prasad

Reputation: 171

jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;

Keeping this in the block solved my issue.

it('', () => {
 jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
});

Upvotes: 0

SoEzPz
SoEzPz

Reputation: 15922

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(){
  //...
});

Upvotes: 0

delliottg
delliottg

Reputation: 4150

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.

Upvotes: 6

A. Figueroa
A. Figueroa

Reputation: 225

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 :)

Upvotes: 2

Lijo
Lijo

Reputation: 6788

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

Upvotes: 20

Tom Van Rossom
Tom Van Rossom

Reputation: 1470

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 () {

    }
}

Upvotes: 3

Eric Soyke
Eric Soyke

Reputation: 1067

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.

Upvotes: 5

Related Questions