Jasmin: Der asynchrone Rückruf wurde nicht innerhalb des von jasmine.DEFAULT_TIMEOUT_INTERVAL angegebenen Zeitlimits aufgerufen

139

Ich habe einen Winkelservice namens 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
    };

});

Ich versuche, diesen Dienst mit Jasmin zu testen:

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

Ich habe über die asynchrone Unterstützung in Jasmine gelesen, aber da ich noch nicht mit Unit-Tests mit Javascript vertraut bin, konnte dies nicht funktionieren.

Ich erhalte eine Fehlermeldung:

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

und mein Test dauert zu lange (ca. 5s).

Kann mir jemand helfen, ein funktionierendes Beispiel für meinen Code mit einer Erklärung zu liefern?

Mdb
quelle
1
Die Verarbeitung von Ereignissen erfolgt normalerweise in einem Digest-Zyklus. Versuchen Sie, Ihrem Test den Bereich. $ Apply () hinzuzufügen, anstatt Jasmines asynchrones Testmuster zu verwenden
Eitan Peer
das hat nicht funktioniert. Ich habe den Bereich hinzugefügt. $ Apply (); kurz nach dem Aufruf von requestNotificationChannel.deleteMessage (1, 4), aber ich erhalte den gleichen Fehler ...
Mdb
Ich erhalte den gleichen Fehler, wenn die Ausführung von asynchronen Tests längerJest dauert als erwartet - sehr häufig beim Debuggen und beim Überprüfen der Variablen.
Dan Dascalescu
Versuchen Sie stattdessen, eine geringere Zeitüberschreitung zu verwenden. Ich habe diesen Fehler bei der Verwendung von timeout = 5000 erhalten. Ich habe ihn durch 2000 ersetzt und er hat bei mir funktioniert!
Marina Riaz
1
Lassen Sie dies hier, um jemandem in meinen Schuhen zu helfen. Ich hatte diesen Fehler beim Ausführen von Tests in einem Docker-Container. Tests bestanden manchmal ohne Probleme, scheiterten aber manchmal. Ich dachte, es sei eine Art Rennbedingung, konnte aber nicht herausfinden, warum. Mir wurde klar, dass ich einen afterEachSchritt hatte, bei dem die Datenbank gelöscht wurde (mithilfe der deleteManyMethode). Das Hinzufügen jest.setTimeout(30000);der beforeAllMethode scheint dies für mich behoben zu haben - ich vermute, da das Löschen der Datenbank ein Netzwerkaufruf ist (innerhalb der Bedingung), dauerte es manchmal länger als 3 Sekunden und warf.
nkhil

Antworten:

230

Wenn Sie ein Argument in Ihrer itFunktion haben ( doneim folgenden Code), versucht Jasmine einen asynchronen Aufruf.

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

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

Es macht keinen Unterschied, wie das doneArgument benannt wird, seine Existenz ist alles, was zählt. Ich bin auf dieses Problem durch zu viele Kopien / Nudeln gestoßen.

In den Dokumenten zur asynchronen Unterstützung von Jasmine wird darauf hingewiesen, dass das doneoben genannte Argument ein Rückruf ist, der aufgerufen werden kann, um Jasmine über den Abschluss einer asynchronen Funktion zu informieren. Wenn Sie es nie anrufen, wird Jasmine nie wissen, dass Ihr Test abgeschlossen ist, und wird irgendwann eine Zeitüberschreitung haben.

mastaBlasta
quelle
3
Das gleiche gilt für Argumente in beschreiben (in eckigen, müssen Sie injizieren in beschreiben nennen, um das zu tun)
Narretz
@ MartinBliss Es ist dokumentiert, ich habe gerade eine Änderung vorgeschlagen, um auf die Dokumentation zu verweisen: stackoverflow.com/suggested-edits/2434606
Vincent
39
Hinweis für zufällige Googler, die in Zukunft auf diese Frage stoßen: Wenn Sie Protractor verwenden und auf dieses Problem stoßen, ist diese Antwort nicht das, wonach Sie suchen - Protractor ruft den Rückruf von selbst auf.
Vincent
Es hat mein Problem behoben und wurde durch das gleiche Cuplrit "Kopie / Pasta" verursacht
Shaikh
1
@ Vincent, was geben die Winkelmesser-Benutzer dann aus, wenn dieser Fehler auftritt?
Bruno Bieri
57

Selbst bei asynchronen Tests tritt in diesem Fall eine Zeitüberschreitung auf. Sie können diesen Fehler umgehen, indem Sie den Wert für die Grenzzeitüberschreitung erhöhen, um einen asynchronen Jasmine-Rückruf auszuwerten

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

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

gsalgadotoledo
quelle
1
Dies scheint nicht "der richtige Weg" zu sein, aber nachdem ich ein paar zusätzliche Nullen hinzugefügt hatte, damit mein Selenium-Test ausgeführt werden konnte, war dies ein notwendiger Hack.
Schmirgel
Der ursprüngliche Jasmin.DEFAULT_TIMEOUT_INTERVAL beträgt 60000 ms. Dieses Beispiel wird es also tatsächlich sechsmal kürzer machen.
Waltari
Du hast recht, ich habe nur eine Zufallszahl in dieses Beispiel gesetzt, danke :)
gsalgadotoledo
20

Dieser Fehler kann auch dadurch verursacht werden, dass beim Initialisieren eines Service / einer Fabrik oder was auch immer die Injektion weggelassen wird. Zum Beispiel kann es folgendermaßen geworfen werden:

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

Um dies zu beheben, schließen Sie die Funktion einfach mit inj ein, um den Dienst ordnungsgemäß abzurufen:

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

benutze fakeAsync

beforeEach(fakeAsync (() => {

//your code

}));



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

Sie können Karma-Jasmin verwenden Plugin verwenden, um das Standard-Timeout-Intervall global festzulegen.

Fügen Sie diese Konfiguration in karma.conf.js hinzu

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

Dieser Fehler begann für mich aus heiterem Himmel bei einem Test, der immer funktioniert hatte. Ich konnte keine Vorschläge finden, die mir halfen, bis ich bemerkte, dass mein Macbook nur schleppend lief. Ich bemerkte, dass die CPU an einen anderen Prozess gebunden war, den ich beendet hatte. Der Jasmine-Async-Fehler ist verschwunden und meine Tests sind wieder in Ordnung.

Frag mich nicht warum, ich weiß es nicht. Aber unter meinen Umständen schien es ein Mangel an fehlerhaften Systemressourcen zu sein.

Eric Soyke
quelle
5
Wenn Ihre CPU frei war, wurde die Aufgabe wahrscheinlich vor dem Standardzeitlimit beendet. Wenn die CPU ausgelastet war, dauerte die zu testende Aufgabe zu lange.
Shane
5

Dies ist eher eine Beobachtung als eine Antwort, aber es kann anderen helfen, die genauso frustriert waren wie ich.

Ich habe diesen Fehler immer wieder von zwei Tests in meiner Suite erhalten. Ich dachte, ich hätte die Tests mit dem von mir durchgeführten Refactoring einfach abgebrochen. Nachdem das Zurücksetzen von Änderungen nicht funktioniert hatte, kehrte ich zweimal (zwei Revisionen zurück) zu früherem Code zurück und dachte, der Fehler würde dadurch behoben. Dies änderte nichts. Ich habe gestern den ganzen Tag meinen Schwanz gejagt und einen Teil dieses Morgens, ohne das Problem zu lösen.

Ich war frustriert und habe heute Morgen den Code auf einem Laptop überprüft. Lief die gesamte Testsuite (ca. 180 Tests), keine Fehler. Die Fehler waren also nie im Code oder in den Tests. Ging zurück zu meiner Entwicklungsbox und startete sie neu, um alles im Speicher zu löschen, was das Problem verursacht haben könnte. Keine Änderung, gleiche Fehler bei den gleichen beiden Tests. Also habe ich das Verzeichnis von meinem Computer gelöscht und es wieder ausgecheckt. Voila! Keine Fehler.

Keine Ahnung, was es verursacht hat oder wie es behoben werden kann, aber das Löschen des Arbeitsverzeichnisses und das erneute Auschecken haben das Problem behoben.

Hoffe das hilft jemandem.

delliottg
quelle
1
Danke Mann, ich war verrückt danach. Ich habe meinen PC neu gestartet und das
wars
In meinem Fall habe ich den Befehl einfach erneut ausgeführt und das Problem behoben. Ich hatte heißes Nachladen für die Unit-Tests und jedes Mal, wenn es fehlschlug. Ich musste anhalten und den Befehl erneut ausführen.
Jignesh
4

Nicht verwenden done, einfach den Funktionsaufruf leer lassen.

Anastasiia Semionova
quelle
Bitte korrigieren Sie mich, wenn ich falsch liege, aber wie ich verstanden habe, wird die Testsuite erst vor dem Test beendet und stattdessen wird die Fehlermeldung ausgegeben. Dies bedeutet, dass eine fehlgeschlagene Zusicherung den Test nicht unterbricht, wenn die Testsuite beendet ist, bevor die Zusicherung ausgeführt wird. Es könnte auch bedeuten (ich habe ein ähnliches Verhalten gesehen), dass ein anderer Test den Fehler zeigt, den dieser Test erzeugt hat. Schließlich bedeutet dies, dass zunächst alles in Ordnung aussieht, aber mit zunehmender Anzahl von Tests tritt das Problem zeitweise auf.
LosManos
3

Sie erhalten diesen Fehler auch, wenn Sie etwas in der beforeAllFunktion erwarten !

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

    beforeAll(function () {
        ...

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

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

    }
}
Tom Van Rossom
quelle
2

In meinem Fall wurde dieser Fehler durch die missbräuchliche Verwendung von "fixture.detectChanges ()" verursacht. Es scheint, dass diese Methode ein Ereignis-Listener (asynchron) ist, der nur dann auf einen Rückruf reagiert, wenn Änderungen erkannt werden. Wenn keine Änderungen festgestellt werden, wird der Rückruf nicht aufgerufen, was zu einem Timeout-Fehler führt. Hoffe das hilft :)

A. Figueroa
quelle
2

Funktioniert nach dem Entfernen der scopeReferenz und der Funktionsargumente:

"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
quelle
0

Wie von @mastablasta bemerkt, aber um hinzuzufügen, dass Sie, wenn Sie das Argument 'done' aufrufen oder es als abgeschlossen bezeichnen , einfach den Rückruf complete () in Ihrem Test aufrufen, wenn es fertig ist.

// 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
quelle
0

jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;

Dies im Block zu belassen, löste mein Problem.

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

Was ich getan habe war: Den folgenden Code hinzugefügt / aktualisiert:

framework: 'jasmine',
jasmineNodeOpts: 
{
    // Jasmine default timeout
    defaultTimeoutInterval: 60000,
    expectationResultHandler(passed, assertion) 
    {
      // do something
    },
}
Zeeshan
quelle
3
Bitte erläutern Sie, warum dieser Code funktioniert, anstatt ihn nur ohne Erklärung zu veröffentlichen.
Kobe
Wenn Sie also einen Test ausführen und dieser länger als erwartet dauert, schlägt er fehl, da das Standardzeitlimit eingehalten wurde und das Skript bei der Ausführung nicht vorwärts ging. Dies kann passieren, weil einige Bedingungen nicht erfüllt sind (z. B. Sichtbarkeit, Laden von Seiten). Wenn Ihr Standardzeitlimit 1000 ms beträgt >>, würden Skripte häufig fehlschlagen, da es nur eine Sekunde dauert und mehrere Faktoren zum Ausfall Ihres Skripts beitragen können. Wenn Sie jedoch das Zeitlimit erhöhen, kann der Browser / Treiber länger warten, bis die Bedingungen erfüllt sind.
Zeeshan
2
Okay, jetzt schreibe das in deinen Beitrag. Sie sollten versuchen zu vermeiden, nur mit Code ohne Erklärung zu antworten :)
Kobe
0

Anstatt

beforeEach(() => {..

verwenden

beforeEach(fakeAsync(() => {..
Meghnath Das
quelle
0

Es sieht so aus, als würde der Test auf einen Rückruf warten, der niemals kommt. Dies liegt wahrscheinlich daran, dass der Test nicht mit asynchronem Verhalten ausgeführt wird.

Überprüfen Sie zunächst, ob Sie in Ihrem "it" -Szenario nur fakeAsync verwenden:

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

Sie können auch flush()warten, bis die microTask-Warteschlange beendet ist, oder tick()eine bestimmte Zeit warten.

Gestaltwandler
quelle
-2

Wenn Sie ein Argument ( done) in der itFunktion haben, versuchen Sie es ebenfalls zu entfernen. Der Aufruf erfolgt innerhalb der Funktion selbst:

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
quelle
4
Ohne eine Erklärung, warum diese Antwort nicht so nützlich ist.
Gary
2
Diese Antwort hat mein Problem gelöst ... Winkeltests sind ein Albtraum!
Benjamin Caure