jasmine: il callback asincrono non è stato richiamato entro il timeout specificato da jasmine.DEFAULT_TIMEOUT_INTERVAL


141

Ho un servizio angolare chiamato 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
    };

});

Sto cercando di testare l'unità di questo servizio usando 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();       
    });
});

Ho letto del supporto asincrono in Jasmine, ma dato che sono piuttosto nuovo ai test unitari con javascript non sono riuscito a farlo funzionare.

Ricevo un errore:

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

e il mio test sta impiegando troppo tempo per essere eseguito (circa 5 secondi).

Qualcuno può aiutarmi a fornire un esempio funzionante del mio codice con qualche spiegazione?


1
L'elaborazione degli eventi viene generalmente eseguita in un ciclo digest. Prova ad aggiungere scope. $ Apply () al tuo test invece di usare il modello di test asincrono di Jasmine
Eitan Peer

questo non ha funzionato. Ho aggiunto scope. $ Apply (); subito dopo aver chiamato requestNotificationChannel.deleteMessage (1, 4) ma ho ricevuto lo stesso errore ...
Mdb

Ottengo lo stesso errore quando l' esecuzione dei test asincroni richiede più tempo del Jestprevisto , molto comune durante il debug e il tempo impiegato per ispezionare le variabili.
Dan Dascalescu l'

Prova invece a utilizzare un timeout inferiore. Ho riscontrato questo errore durante l'utilizzo di timeout = 5000. L'ho sostituito con 2000 e ha funzionato per me!
Marina Riaz,

1
Lasciando questo qui per aiutare qualcuno nei miei panni. Ho riscontrato questo errore durante l'esecuzione dei test all'interno di un contenitore finestra mobile. I test a volte passavano senza problemi, ma a volte fallivano. Ho pensato che fosse una specie di condizione di gara, ma non riuscivo a capire perché. Mi sono reso conto che avevo un afterEachpasso che stava cancellando il database (usando il deleteManymetodo). L'aggiunta jest.setTimeout(30000);del beforeAllmetodo sembra aver risolto questo problema per me - suppongo che l'eliminazione del database sia una chiamata di rete (all'interno della condizione), a volte impiegava più di 3 secondi e il lancio.
nkhil,

Risposte:


231

Avere un argomento nella tua itfunzione ( donenel codice seguente) farà provare a una chiamata asincrona a Jasmine.

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

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

Non fa differenza il donenome dell'argomento, la sua esistenza è tutto ciò che conta. Ho riscontrato questo problema da troppa copia / pasta.

I documenti di supporto asincrono Jasmine notano che l'argomento ( donesopra citato) è un callback che può essere chiamato per far sapere a Jasmine quando una funzione asincrona è completa. Se non lo chiami mai, Jasmine non saprà mai che il test è stato eseguito e alla fine scadrà.


3
Lo stesso vale per args in descritto (in angolare, è necessario chiamare iniettare in descrivi per farlo)
Narretz,

@MartinBliss È documentato, ho appena suggerito una modifica per fare riferimento alla documentazione: stackoverflow.com/suggested-edits/2434606
Vincent

39
Nota per i googler casuali che incontrano questa domanda in futuro: se stai usando Protractor e ti imbatti in questo problema, questa risposta non è ciò che stai cercando: Protractor chiama il callback da solo.
Vincent,

Risolveva il mio problema ed era causato dallo stesso cuplrit "copia / pasta"
shaikh

1
@Vincent qual è il problema degli utenti di Protractor se questo errore si verifica?
Bruno Bieri,

57

Anche per i test asincroni, esiste un timeout che si interrompe in questi casi, è possibile aggirare questo errore aumentando il valore del timeout limite per valutare un callback asincrono di 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();
            }
        });
    });
});

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


1
Questo non sembra "il modo giusto" per farlo, ma dopo aver aggiunto un paio di zeri in più per l'esecuzione del mio test del selenio, questo era un trucco necessario.
emery

Il gelsomino originale.DEFAULT_TIMEOUT_INTERVAL è 60000 ms. Quindi questo esempio lo renderà effettivamente sei volte più breve.
Waltari,

Hai ragione, ho appena inserito un numero casuale in questo esempio, grazie :)
gsalgadotoledo,

20

Questo errore può essere causato anche tralasciando l'iniezione durante l'inizializzazione di un servizio / fabbrica o altro. Ad esempio, può essere lanciato in questo modo:

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

Per risolverlo basta avvolgere la funzione con inject per recuperare correttamente il servizio:

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

13
import { fakeAsync, ComponentFixture, TestBed } from '@angular/core/testing';

usa fakeAsync

beforeEach(fakeAsync (() => {

//your code

}));



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

6

È possibile utilizzare il plugin karma-jasmine per impostare l'intervallo di timeout predefinito a livello globale.

Aggiungi questa configurazione in karma.conf.js

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

5

Questo errore è iniziato all'improvviso per me, su un test che aveva sempre funzionato. Non sono riuscito a trovare alcun suggerimento che mi abbia aiutato fino a quando non ho notato che il mio Macbook funzionava lentamente. Ho notato che la CPU è stata ancorata da un altro processo, che ho ucciso. L'errore asincrono di Jasmine è scomparso e i miei test vanno di nuovo bene.

Non chiedermi perché, non lo so. Ma nelle mie circostanze sembrava essere la mancanza di risorse di sistema in difetto.


5
Probabilmente, quando la CPU era libera, l'attività è terminata prima del timeout predefinito. Quando la CPU era occupata, l'attività che stavi testando impiegava troppo tempo per essere completata.
Shane,

5

Questa è più un'osservazione che una risposta, ma può aiutare gli altri che erano frustrati quanto me.

Ho continuato a ricevere questo errore da due test nella mia suite. Pensavo di aver semplicemente interrotto i test con il refactoring che stavo facendo, quindi dopo il backup delle modifiche non ha funzionato, sono tornato al codice precedente, due volte (due revisioni indietro) pensando che si sarebbe sbarazzato dell'errore. In questo modo non è cambiato nulla. Ho inseguito la coda tutto il giorno ieri e parte di questa mattina senza risolvere il problema.

Sono stato frustrato e ho controllato il codice su un laptop questa mattina. Ha eseguito l'intera suite di test (circa 180 test), senza errori. Quindi gli errori non sono mai stati nel codice o nei test. Sono tornato alla mia casella di sviluppo e lo ho riavviato per cancellare qualsiasi cosa in memoria che potrebbe aver causato il problema. Nessuna modifica, stessi errori negli stessi due test. Quindi ho eliminato la directory dal mio computer e l'ho ricontrollata. Ecco! Nessun errore

Non ho idea di cosa l'abbia causato o di come risolverlo, ma l'eliminazione della directory di lavoro e il suo controllo hanno risolto qualunque cosa fosse.

Spero che questo aiuti qualcuno.


1
Grazie amico, stavo impazzendo per questo. Ho riavviato il mio PC e basta
yngrdyn

Nel mio caso, ho appena rieseguito il comando e questo problema è stato risolto. Ho avuto una ricarica a caldo per i test unitari e ogni volta che non funzionava. Ho dovuto fermarmi ed eseguire nuovamente il comando.
jignesh,

4

Non usare done, lascia vuota la chiamata di funzione.


Per favore, correggimi se sbaglio ma, come ho capito, fa terminare la suite di test prima del test stesso e viene invece scaricato il messaggio di errore. Ciò significa che qualsiasi asserzione fallita non interromperà il test poiché la suite di test è terminata prima dell'esecuzione dell'asserzione. Potrebbe anche significare (ho visto un comportamento simile) che un altro test mostra l'errore creato da questo test. Alla fine significa che tutto sembra a posto, ma con l'aumentare del numero di test il problema si aprirà in modo intermittente.
LosManos,

3

Questo errore viene visualizzato anche quando ci si aspetta qualcosa nella beforeAllfunzione!

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

    beforeAll(function () {
        ...

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

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

    }
}

2

Nel mio caso, questo errore è stato causato dall'uso improprio di "fixture.detectChanges ()" Sembra che questo metodo sia un listener di eventi (asincrono) che risponderà a una richiamata solo quando vengono rilevate modifiche. Se non vengono rilevate modifiche, non verrà richiamato il callback, con conseguente errore di timeout. Spero che questo ti aiuti :)


2

Funziona dopo aver rimosso il scoperiferimento e gli argomenti della funzione:

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

0

Come notato da @mastablasta, ma anche per aggiungere che se si chiama l'argomento 'done' o piuttosto il nome è completato , al termine del test è sufficiente chiamare il callback completato ().

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

0

jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;

Mantenere questo nel blocco ha risolto il mio problema.

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

0

Quello che ho fatto è stato: aggiunto / aggiornato il seguente codice:

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

3
Spiega perché questo codice funziona, piuttosto che pubblicarlo senza una spiegazione.
Kobe,

Quindi, fondamentalmente, quando si esegue un test e richiede più tempo del previsto, si verifica un errore poiché il timeout predefinito è stato rispettato e lo script non è andato avanti nell'esecuzione. Ciò potrebbe accadere a causa di alcune condizioni non soddisfatte (ad esempio visibilità, caricamento della pagina). Ora se il timeout predefinito è come 1000 ms >> gli script fallirebbero spesso perché è solo un secondo e potrebbero esserci più fattori che si aggiungono al fallimento dello script. Tuttavia, aumentare l'intervallo di timeout può far attendere più a lungo il browser / driver affinché le condizioni siano soddisfatte.
Zeeshan,

2
Bene, ora scrivilo nel tuo post; dovresti cercare di evitare di rispondere semplicemente con il codice senza spiegazioni :)
Kobe,


0

Sembra che il test stia aspettando un callback che non arriva mai. È probabile perché il test non viene eseguito con comportamento asincrono.

Per prima cosa, vedi se stai usando fakeAsync nel tuo scenario "it":

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

È inoltre possibile utilizzare flush()per attendere il completamento della coda microTask o tick()per attendere un determinato periodo di tempo.


-2

Se hai un argomento ( done) nella itfunzione prova anche a rimuoverlo, si chiama all'interno della funzione stessa:

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

4
Senza alcuna spiegazione del perché questa risposta non sia così utile.
Gary,

2
questa risposta ha risolto il mio problema ... i test angolari sono un incubo!
Benjamin Caure,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.