jasmine: le rappel Async n'a pas été appelé dans le délai spécifié par jasmine.DEFAULT_TIMEOUT_INTERVAL

141

J'ai un service angulaire appelé 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
    };

});

J'essaye de tester ce service unitaire en utilisant 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();       
    });
});

J'ai lu sur le support asynchrone dans Jasmine, mais comme je suis plutôt nouveau dans les tests unitaires avec javascript, cela n'a pas pu le faire fonctionner.

Je reçois une erreur:

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

et mon test prend trop de temps à exécuter (environ 5 s).

Quelqu'un peut-il m'aider à fournir un exemple fonctionnel de mon code avec quelques explications?

Mdb
la source
1
Le traitement des événements est généralement effectué dans un cycle de résumé. Essayez d'ajouter scope. $ Apply () à votre test au lieu d'utiliser le modèle de test asynchrone de Jasmine
Eitan Peer
cela n'a pas fonctionné. J'ai ajouté la portée. $ Apply (); juste après avoir appelé requestNotificationChannel.deleteMessage (1, 4) mais je reçois la même erreur ...
Mdb
J'obtiens la même erreur lorsque les tests asynchrones prennent plus de temps à s'exécuter que Jestprévu - très courant lors du débogage et en prenant un certain temps pour inspecter les variables.
Dan Dascalescu
Essayez plutôt d'utiliser un délai d'expiration moindre. J'ai eu cette erreur en utilisant timeout = 5000. Je l'ai remplacé par 2000 et cela a fonctionné pour moi!
Marina Riaz
1
Laisser ça ici pour aider quelqu'un à ma place. J'ai eu cette erreur lors de l'exécution de tests à l'intérieur d'un conteneur docker. Les tests réussissaient parfois sans aucun problème, mais échouaient parfois. J'ai pensé que c'était une sorte de condition de course, mais je ne pouvais pas comprendre pourquoi. J'ai réalisé que j'avais une afterEachétape qui effaçait la base de données (en utilisant la deleteManyméthode). L'ajout jest.setTimeout(30000);de la beforeAllméthode semble avoir résolu cela pour moi - je suppose que puisque la suppression de la base de données est un appel réseau (dans la condition), cela prenait parfois plus de 3 secondes et le jetait.
nkhil le

Réponses:

231

Avoir un argument dans votre itfonction ( donedans le code ci-dessous) obligera Jasmine à tenter un appel asynchrone.

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

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

Le donenom de l' argument ne fait aucune différence , son existence est tout ce qui compte. J'ai rencontré ce problème à cause d'un trop grand nombre de copies / pâtes.

La documentation du support asynchrone de Jasmine note que l'argument (nommé doneci-dessus) est un rappel qui peut être appelé pour informer Jasmine lorsqu'une fonction asynchrone est terminée. Si vous ne l'appelez jamais, Jasmine ne saura jamais que votre test est terminé et finira par expirer.

mastaBlasta
la source
3
La même chose est vraie pour les arguments dans describe (en angular, vous devez appeler inject in describe pour le faire)
Narretz
@MartinBliss C'est documenté, je viens de suggérer une modification pour se référer à la documentation: stackoverflow.com/suggested-edits/2434606
Vincent
39
Note aux Googleurs aléatoires qui rencontreront cette question à l'avenir: si vous utilisez Protractor et que vous rencontrez ce problème, cette réponse n'est pas ce que vous recherchez - Protractor appelle le rappel par lui-même.
Vincent
Cela a résolu mon problème et il a été causé par le même cuplrit "copie / pâtes"
shaikh
1
@Vincent quel est le problème des utilisateurs de Protractor alors si cette erreur se produit?
Bruno Bieri
57

Même pour les tests asynchrones, il existe un délai d'expiration qui se déclenche dans ce cas.Vous pouvez contourner cette erreur en augmentant la valeur du délai d'expiration limite pour évaluer un rappel asynchrone Jasmine

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
la source
1
Cela ne semble pas "la bonne façon" de le faire, mais après avoir ajouté quelques zéros supplémentaires pour que mon test Selenium s'exécute, c'était un hack nécessaire.
emery
Le jasmin d'origine.DEFAULT_TIMEOUT_INTERVAL est de 60000 ms. Donc, cet exemple le rendra six fois plus court.
Waltari
Vous avez raison, je viens de mettre un nombre aléatoire dans cet exemple, merci :)
gsalgadotoledo
20

Cette erreur peut également être causée en omettant inject lors de l'initialisation d'un service / usine ou autre. Par exemple, il peut être lancé en faisant ceci:

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

Pour résoudre ce problème, enveloppez simplement la fonction avec inject pour récupérer correctement le service:

var service;
beforeEach(inject(function(_TestService_) {
    service = _TestService_;
}));
Katana24
la source
13
import { fakeAsync, ComponentFixture, TestBed } from '@angular/core/testing';

utiliser fakeAsync

beforeEach(fakeAsync (() => {

//your code

}));



describe('Intilalize', () => {
        it('should have a defined component', fakeAsync(() => {
            createComponent();
            expect(_AddComponent.ngOnInit).toBeDefined();
        }));
    });
Lijo
la source
6

Vous pouvez utiliser le plugin karma-jasmine pour définir globalement l'intervalle de temporisation par défaut.

Ajoutez cette configuration dans karma.conf.js

module.exports = function(config) {
  config.set({
    client: {
      jasmine: {
        timeoutInterval: 10000
      }
    }
  })
}
code.rookie
la source
5

Cette erreur a commencé à l'improviste pour moi, sur un test qui avait toujours fonctionné. Je n'ai trouvé aucune suggestion qui m'a aidé jusqu'à ce que je remarque que mon Macbook fonctionnait lentement. J'ai remarqué que le processeur était lié à un autre processus, que j'ai tué. L'erreur asynchrone Jasmine a disparu et mes tests sont à nouveau bien.

Ne me demandez pas pourquoi, je ne sais pas. Mais dans ma situation, cela semblait être un manque de ressources système en faute.

Eric Soyke
la source
5
Probablement, lorsque votre processeur était libre, la tâche s'est terminée avant le délai d'expiration par défaut. Lorsque le processeur était occupé, la tâche que vous testiez prenait trop de temps.
Shane
5

C'est plus une observation qu'une réponse, mais cela peut aider d'autres qui étaient aussi frustrés que moi.

J'ai continué à recevoir cette erreur de deux tests dans ma suite. Je pensais que j'avais simplement cassé les tests avec la refactorisation que je faisais, donc après que la sauvegarde des modifications ne fonctionnait pas, je suis revenu au code précédent, deux fois (deux révisions en arrière) en pensant qu'il éliminerait l'erreur. Cela n'a rien changé. J'ai chassé ma queue toute la journée d'hier, et une partie de ce matin sans résoudre le problème.

J'ai été frustré et j'ai vérifié le code sur un ordinateur portable ce matin. Ran toute la suite de tests (environ 180 tests), aucune erreur. Donc, les erreurs n'étaient jamais dans le code ou les tests. Je suis retourné dans ma boîte de développement et je l'ai redémarré pour effacer tout ce qui pouvait être à l'origine du problème en mémoire. Aucun changement, mêmes erreurs sur les deux mêmes tests. J'ai donc supprimé le répertoire de ma machine et je l'ai récupéré. Voila! Aucune erreur.

Aucune idée de ce qui l'a causé, ni de la façon de le réparer, mais la suppression du répertoire de travail et sa réévaluation ont corrigé ce que c'était.

J'espère que cela aide quelqu'un.

delliottg
la source
1
Merci mec, je devenais fou à ce sujet. J'ai redémarré mon PC et c'était tout
yngrdyn
Dans mon cas, je viens de relancer la commande et cela a résolu ce problème. J'ai eu un rechargement à chaud pour les tests unitaires et à chaque fois cela échouait. J'ai dû m'arrêter et exécuter à nouveau la commande.
jignesh
4

N'utilisez pas done, laissez simplement l'appel de fonction vide.

Anastasiia Semionova
la source
Veuillez me corriger si je me trompe, mais comme je l'ai compris, la suite de tests se termine juste avant le test et le message d'erreur est vidé à la place. Ce qui signifie que toute assertion qui échoue n'interrompra pas le test car la suite de tests est terminée avant l'exécution de l'assert. Cela pourrait également signifier (j'ai vu un comportement similaire) qu'un autre test montre l'erreur que ce test a créée. Enfin, cela signifie que tout semble correct pour commencer, mais à mesure que le nombre de tests augmente, le problème apparaîtra par intermittence.
LosManos
3

Vous obtenez également cette erreur lorsque vous attendez quelque chose dans la beforeAllfonction!

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

    beforeAll(function () {
        ...

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

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

    }
}
Tom Van Rossom
la source
2

Dans mon cas, cette erreur a été causée par une mauvaise utilisation de "fixture.detectChanges ()". Il semble que cette méthode est un écouteur d'événements (async) qui ne répondra à un rappel que lorsque des modifications seront détectées. Si aucun changement n'est détecté, il n'appellera pas le rappel, ce qui entraînera une erreur de temporisation. J'espère que cela t'aides :)

A. Figueroa
la source
2

Fonctionne après avoir supprimé la scoperéférence et les arguments de la fonction:

"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} );
    });
});
Paul Sweatte
la source
0

Comme noté par @mastablasta, mais aussi pour ajouter que si vous appelez l'argument 'done' ou plutôt nommez-le terminé, vous appelez simplement le callback completed () dans votre test lorsque c'est fait.

// 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
la source
0

jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;

Garder cela dans le bloc a résolu mon problème.

it('', () => {
 jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
});
Sai Prasad
la source
0

Ce que j'ai fait, c'est: Ajout / mise à jour du code suivant:

framework: 'jasmine',
jasmineNodeOpts: 
{
    // Jasmine default timeout
    defaultTimeoutInterval: 60000,
    expectationResultHandler(passed, assertion) 
    {
      // do something
    },
}
Zeeshan
la source
3
Veuillez expliquer pourquoi ce code fonctionne, plutôt que de simplement le publier sans explication.
Kobe
Donc, fondamentalement, lorsque vous exécutez un test et que cela prend plus de temps que prévu, il échoue car le délai par défaut a été respecté et le script n'a pas avancé dans l'exécution. Cela peut se produire en raison de certaines conditions non remplies (par exemple, visibilité, chargement de page). Maintenant, si votre délai d'expiration par défaut est de 1000 ms >>, les scripts échouent souvent car il ne s'agit que d'une seconde et plusieurs facteurs peuvent contribuer à l'échec de votre script. Cependant, l'augmentation de votre délai d'expiration peut faire attendre le navigateur / pilote plus longtemps pour que les conditions soient remplies.
Zeeshan
2
D'accord, maintenant inscrivez-le dans votre message; vous devriez essayer d'éviter de simplement répondre avec du code sans explication :)
Kobe
0

Au lieu de

beforeEach(() => {..

utilisation

beforeEach(fakeAsync(() => {..
Meghnath Das
la source
0

Il semble que le test attend un rappel qui ne vient jamais. C'est probablement parce que le test n'est pas exécuté avec un comportement asynchrone.

Tout d'abord, voyez si vous utilisez simplement fakeAsync dans votre scénario "it":

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

Vous pouvez également utiliser flush()pour attendre la fin de la file d'attente des microTaches ou tick()pour attendre un laps de temps spécifié.

Shapeshifter
la source
-2

Si vous avez un argument ( done) dans la itfonction, essayez de le supprimer également, il est appelé dans la fonction elle-même:

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        
});
tiagor87
la source
4
Sans aucune explication sur les raisons pour lesquelles cette réponse n'est pas si utile.
Gary
2
cette réponse a résolu mon problème ... le test angulaire est un cauchemar!
Benjamin Caure