jazmín: no se invocó la devolución de llamada asíncrona dentro del tiempo de espera especificado por jasmine.DEFAULT_TIMEOUT_INTERVAL


141

Tengo un servicio angular llamado 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
    };

});

Estoy tratando de probar este servicio con jazmín:

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

Leí sobre el soporte asíncrono en Jasmine, pero como soy bastante nuevo en las pruebas unitarias con javascript no pude hacerlo funcionar.

Estoy recibiendo un error:

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

y mi prueba está tardando demasiado en ejecutarse (aproximadamente 5 segundos).

¿Alguien puede ayudarme a proporcionar un ejemplo funcional de mi código con alguna explicación?


1
El procesamiento de eventos generalmente se realiza en un ciclo de resumen. Intente agregar el alcance. $ Apply () a su prueba en lugar de usar el patrón de prueba asíncrono de Jasmine
Eitan Peer

Esto no funcionó. Agregué alcance. $ Apply (); justo después de llamar a requestNotificationChannel.deleteMessage (1, 4) pero recibo el mismo error ...
Mdb

Me sale el mismo error cuando las pruebas asíncronas tardan más en ejecutarse de lo Jestesperado , muy común al depurar y tomar algo de tiempo para inspeccionar las variables.
Dan Dascalescu

Intente usar menos tiempo de espera en su lugar. Recibí este error al usar timeout = 5000. ¡Lo reemplacé con 2000 y funcionó para mí!
Marina Riaz

1
Dejando esto aquí para ayudar a alguien en mis zapatos. Tuve este error al ejecutar pruebas dentro de un contenedor acoplable. Las pruebas a veces pasan sin problemas, pero a veces fallan Pensé que era una especie de condición de carrera, pero no podía entender por qué. Me di cuenta de que tenía un afterEachpaso que estaba limpiando la base de datos (usando el deleteManymétodo). Agregar jest.setTimeout(30000);el beforeAllmétodo parece haber solucionado esto para mí: supongo que dado que la eliminación de la base de datos es una llamada de red (dentro de la condición), a veces tomaba más de 3 segundos y se lanzaba.
nkhil

Respuestas:


231

Tener un argumento en su itfunción ( doneen el código a continuación) hará que Jasmine intente una llamada asincrónica.

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

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

No importa cómo donese llame el argumento, su existencia es todo lo que importa. Me encontré con este problema por demasiada copia / pasta.

Los documentos de Jasmine Asynchronous Support observan que el argumento (mencionado doneanteriormente) es una devolución de llamada que se puede invocar para que Jasmine sepa cuándo se completa una función asincrónica. Si nunca lo llama, Jasmine nunca sabrá que su prueba está hecha y eventualmente se agotará.


3
Lo mismo es cierto para los argumentos en describe (en angular, debe llamar a inyectar en describe para hacer eso)
Narretz

@MartinBliss Está documentado, acabo de sugerir una edición para consultar la documentación: stackoverflow.com/suggested-edits/2434606
Vincent

39
Nota para los Googlers al azar que se encuentren con esta pregunta en el futuro: si está utilizando Protractor y se encuentra con este problema, esta respuesta no es lo que está buscando: Protractor llama a la devolución de llamada por sí misma.
Vincent

Solucionó mi problema y fue causado por el mismo cuplrit "copy / pasta"
shaikh

1
@Vincent, ¿cuál es el problema de los usuarios de Protractor?
Bruno Bieri

57

Incluso para las pruebas asíncronas, hay un tiempo de espera que se dispara en estos casos. Puede evitar este error aumentando el valor del tiempo límite límite para evaluar una devolución de llamada asincrónica de 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();
            }
        });
    });
});

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


1
Parece que no es "la forma correcta" de hacerlo, pero después de agregar un par de ceros adicionales para que se ejecute mi prueba de Selenium, este fue un truco necesario.
esmeril

El jazmín original.DEFAULT_TIMEOUT_INTERVAL es 60000 ms. Entonces, este ejemplo lo hará seis veces más corto.
Waltari

Tienes razón, acabo de poner un número aleatorio en este ejemplo, gracias :)
gsalgadotoledo

20

Este error también puede ser causado por dejar de inyectar al inicializar un servicio / fábrica o lo que sea. Por ejemplo, se puede lanzar haciendo esto:

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

Para solucionarlo, simplemente envuelva la función con inyectar para recuperar correctamente el servicio:

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

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

usar fakeAsync

beforeEach(fakeAsync (() => {

//your code

}));



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

6

Puedes usar karma-jazmín complemento para establecer el intervalo de tiempo de espera predeterminado globalmente.

Agregue esta configuración en karma.conf.js

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

5

Este error comenzó de la nada para mí, en una prueba que siempre había funcionado. No pude encontrar ninguna sugerencia que me ayudó hasta que noté que mi Macbook se estaba ejecutando lentamente. Noté que la CPU estaba vinculada por otro proceso, que maté. El error asincrónico de Jasmine desapareció y mis pruebas están bien una vez más.

No me preguntes por qué, no lo sé. Pero en mi circunstancia parecía ser la falta de recursos del sistema la culpa.


55
Probablemente, cuando su CPU estaba libre, la tarea finalizó antes del tiempo de espera predeterminado. Cuando la CPU estaba ocupada, la tarea que estaba probando tardó demasiado en completarse.
Shane

5

Esto es más una observación que una respuesta, pero puede ayudar a otros que estaban tan frustrados como yo.

Seguí recibiendo este error de dos pruebas en mi suite. Pensé que simplemente había roto las pruebas con la refactorización que estaba haciendo, por lo que después de retroceder los cambios no funcionó, volví al código anterior, dos veces (dos revisiones anteriores) pensando que eliminaría el error. Hacerlo no cambió nada. Ayer perseguí mi cola todo el día y parte de esta mañana sin resolver el problema.

Me frustré y revisé el código en una computadora portátil esta mañana. Ejecuté todo el conjunto de pruebas (alrededor de 180 pruebas), sin errores. Entonces los errores nunca estuvieron en el código o en las pruebas. Volví a mi cuadro de desarrollo y lo reinicié para borrar cualquier cosa en la memoria que podría haber estado causando el problema. Sin cambios, los mismos errores en las mismas dos pruebas. Así que eliminé el directorio de mi máquina y lo revisé nuevamente. Voila! Sin errores.

No tengo idea de qué lo causó o cómo solucionarlo, pero eliminar el directorio de trabajo y volver a verificarlo solucionó lo que sea.

Espero que esto ayude a alguien.


1
Gracias hombre, me estaba volviendo loco por esto. Reinicié mi PC y eso fue todo
yngrdyn

En mi caso, simplemente volví a ejecutar el comando y resolvió este problema. Tuve una recarga en caliente para las pruebas unitarias y cada vez que fallaba. Tuve que parar y ejecutar el comando nuevamente.
jignesh

4

No lo use done, solo deje la llamada de función vacía.


Corríjame si me equivoco, pero como he entendido, solo hace que el conjunto de pruebas finalice antes de la misma prueba y, en su lugar, se arroja el mensaje de error. Lo que significa que cualquier afirmación que falle no interrumpirá la prueba ya que el conjunto de pruebas finaliza antes de ejecutar la afirmación. También podría significar (he visto un comportamiento similar) que otra prueba muestra el error que creó esta prueba. Finalmente, significa que todo parece estar bien para comenzar, pero a medida que aumenta el número de pruebas, el problema aparecerá de forma intermitente.
LosManos

3

¡También obtiene este error cuando espera algo en la beforeAllfunción!

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

    beforeAll(function () {
        ...

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

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

    }
}

2

En mi caso, este error fue causado por el uso incorrecto de "fixture.detectChanges ()" Parece que este método es un detector de eventos (asíncrono) que solo responderá una devolución de llamada cuando se detecten cambios. Si no se detectan cambios, no invocará la devolución de llamada, lo que provocará un error de tiempo de espera. Espero que esto ayude :)


2

Funciona después de eliminar la scopereferencia y los argumentos de la función:

"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

Como señaló @mastablasta, pero también para agregar que si llama al argumento 'hecho' o más bien lo nombra completado , simplemente llame a la devolución de llamada completada () en su prueba cuando esté hecho.

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

jazmín.DEFAULT_TIMEOUT_INTERVAL = 100000;

Mantener esto en el bloque resolvió mi problema.

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

0

Lo que hice fue: Agregué / actualicé el siguiente código:

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

3
Explique por qué funciona este código, en lugar de simplemente publicarlo sin una explicación.
Kobe

Entonces, básicamente, cuando ejecuta una prueba y tarda más de lo esperado, falla porque se cumplió el tiempo de espera predeterminado y el script no avanzó en la ejecución. Esto podría ocurrir debido a que algunas condiciones no se cumplen (por ejemplo, visibilidad, carga de la página). Ahora, si su tiempo de espera predeterminado es como 1000ms >> los scripts fallarían a menudo porque es solo un segundo y podría haber múltiples factores que se sumen al fallo de su script. Sin embargo, aumentar el intervalo de tiempo de espera puede hacer que el navegador / controlador espere más para que se cumplan las condiciones.
Zeeshan

2
Bien, ahora escribe eso en tu publicación; debe tratar de evitar simplemente responder con código sin explicación :)
Kobe


0

Parece que la prueba está esperando alguna devolución de llamada que nunca llega. Es probable porque la prueba no se ejecuta con un comportamiento asincrónico.

Primero, vea si solo usa fakeAsync en su escenario "it":

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

También puede usar flush()para esperar a que finalice la cola de microTask o tick()para esperar una cantidad de tiempo específica.


-2

Si tiene un argumento ( done) en la itfunción, intente eliminarlo también, se llama dentro de la función en sí:

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

44
Sin ninguna explicación de por qué esta respuesta no es tan útil.
Gary

2
esta respuesta resolvió mi problema ... ¡la prueba angular es una pesadilla!
Benjamin Caure
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.