jasmine: o retorno de chamada assíncrona não foi chamado dentro do tempo limite especificado por jasmine.DEFAULT_TIMEOUT_INTERVAL


140

Eu tenho um serviço angular chamado 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
    };

});

Eu estou tentando testar este serviço usando o jasmim:

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

Eu li sobre o Suporte assíncrono no Jasmine, mas como eu sou novo no teste de unidade com javascript, não poderia fazê-lo funcionar.

Estou recebendo um erro:

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

e meu teste está demorando muito para ser executado (cerca de 5s).

Alguém pode me ajudar a fornecer um exemplo prático do meu código com alguma explicação?


1
O processamento de eventos geralmente é feito em um ciclo de resumo. Tente adicionar âmbito $ aplicar () para o teste em vez de usar padrão de teste assíncrona de Jasmine.
Eitan Par

isso não funcionou. Adicionei escopo. $ Apply (); apenas depois de chamar requestNotificationChannel.deleteMessage (1, 4), mas eu receivethe mesmo erro ...
Mdb

Eu recebo o mesmo erro quando os testes assíncronos levam mais tempo para serem executados do que o Jestesperado - muito comum durante a depuração e o tempo necessário para inspecionar variáveis.
Dan Dascalescu

Tente usar menos tempo limite. Eu recebi esse erro enquanto usava timeout = 5000. Substituí-o por 2000 e funcionou para mim!
Marina Riaz

1
Deixando isso aqui para ajudar alguém no meu lugar. Eu tive esse erro ao executar testes dentro de um contêiner de docker. Às vezes, os testes passam sem problemas, mas às vezes falham. Achei que fosse algum tipo de condição de corrida, mas não consegui descobrir o porquê. Percebi que havia uma afterEachetapa que estava limpando o banco de dados (usando o deleteManymétodo). A adição jest.setTimeout(30000);do beforeAllmétodo parece ter corrigido isso para mim - acho que, como a exclusão do banco de dados é uma chamada de rede (dentro da condição), às vezes levava mais de 3 segundos e o lançamento.
nkhil 13/07

Respostas:


231

Ter um argumento em sua itfunção ( doneno código abaixo) fará com que o Jasmine tente uma chamada assíncrona.

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

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

Não faz diferença o que o done argumento, sua existência é tudo o que importa. Corri para esta questão de muita cópia / massas.

Os documentos do Jasmine Asynchronous Support observam que o argumento (mencionado doneacima) é um retorno de chamada que pode ser chamado para informar o Jasmine quando uma função assíncrona for concluída. Se você nunca ligar, o Jasmine nunca saberá que seu teste está concluído e, eventualmente, atingirá o tempo limite.


3
O mesmo é verdadeiro para argumentos em descrever (em angular, você precisa chamar injetar em descrever a fazer isso)
Narretz

@MartinBliss Ele está documentado, eu justed sugeriu uma edição para consultar a documentação: stackoverflow.com/suggested-edits/2434606
Vincent

39
Observe os Googlers aleatórios que se deparam com essa pergunta no futuro: se você estiver usando o Transferidor e se deparar com esse problema, esta resposta não será o que você está procurando - o Transferidor chama o retorno de chamada sozinho.
Vincent

É fixado o meu problema e ele foi causado pela mesma cuplrit "copiar / massas"
shaikh

1
@Incinc, qual é o problema dos usuários do transferidor, se esse erro ocorrer?
Bruno Bieri

57

Mesmo para testes assíncronos, há um tempo limite que é desativado nesses casos. Você pode solucionar esse erro aumentando o valor do tempo limite para avaliar um retorno de chamada assíncrono do 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
Parece que não é "o caminho certo" para fazer isso, mas depois de adicionar alguns zeros extras para o meu teste de Selenium, esse foi um truque necessário.
esmeril

O jasmine.DEFAULT_TIMEOUT_INTERVAL original é de 60000 ms. Portanto, este exemplo na verdade o tornará seis vezes menor.
Waltari

Você está certo, basta colocar um número aleatório neste exemplo, obrigado :)
gsalgadotoledo

20

Este erro também pode ser causado por deixar de fora a injeção ao inicializar um serviço / fábrica ou qualquer outra coisa. Por exemplo, ele pode ser lançado ao fazer o seguinte:

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

Para corrigi-lo, basta enrolar a função com injetar para recuperar adequadamente o serviço:

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

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

6

Você pode usar karma-jasmim plugin para definir o intervalo de tempo limite padrão globalmente.

Adicione esta configuração em karma.conf.js

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

5

Esse erro começou do nada para mim, em um teste que sempre funcionou. Não consegui encontrar nenhuma sugestão que ajudasse até perceber que meu Macbook estava rodando devagar. Percebi que a CPU estava atrelada a outro processo, que matei. O erro assíncrono do Jasmine desapareceu e meus testes estão bem mais uma vez.

Não me pergunte por que, eu não sei. Mas, na minha circunstância, parecia haver falta de recursos do sistema.


5
Provavelmente, quando sua CPU estava livre, a tarefa terminou antes do tempo limite padrão. Quando a CPU estava ocupada, a tarefa que você estava testando demorou muito para ser concluída.
Shane

5

Isso é mais uma observação do que uma resposta, mas pode ajudar outras pessoas que ficaram tão frustradas quanto eu.

Eu continuei recebendo esse erro de dois testes na minha suíte. Eu pensei que tinha simplesmente quebrado os testes com a refatoração que estava fazendo, então, depois de fazer o backup das alterações não funcionarem, voltei ao código anterior, duas vezes (duas revisões anteriores) pensando que isso iria livrar-se do erro. Fazer isso não mudou nada. Ontem persegui meu rabo o dia todo e parte desta manhã sem resolver o problema.

Fiquei frustrado e verifiquei o código em um laptop esta manhã. Executou todo o conjunto de testes (cerca de 180 testes), sem erros. Portanto, os erros nunca estavam no código ou nos testes. Voltei para minha caixa de desenvolvimento e a reinicializei para limpar qualquer coisa na memória que pudesse estar causando o problema. Nenhuma mudança, os mesmos erros nos mesmos dois testes. Então eu apaguei o diretório da minha máquina e verifiquei novamente. Voila! Sem erros.

Não faço ideia do que o causou ou como corrigi-lo, mas excluir o diretório de trabalho e verificar novamente o que foi corrigido.

Espero que isso ajude alguém.


1
Obrigado cara, eu estava ficando louco por isso. Eu reiniciei meu PC e foi isso
yngrdyn

No meu caso, acabei de executar novamente o comando e ele resolveu esse problema. Eu tinha recarregado a quente para os testes de unidade e toda vez que estava falhando. Eu tive que parar e executar o comando novamente.
jignesh

4

Não use done, apenas deixe a chamada de função vazia.


Corrija-me se estiver errado, mas, como eu entendi, apenas faz o conjunto de testes terminar antes do teste e a mensagem de erro é descartada. O que significa que qualquer declaração que falhar não interromperá o teste, pois o conjunto de testes será concluído antes da execução da declaração. Também pode significar (vi comportamento semelhante) que outro teste mostra o erro que esse teste criou. Finalmente, isso significa que tudo parece bom para começar, mas à medida que o número de testes cresce, o problema aparece intermitentemente.
LosManos

3

Você também recebe esse erro ao esperar algo na beforeAllfunção!

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

    beforeAll(function () {
        ...

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

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

    }
}

2

No meu caso, esse erro foi causado pelo uso inadequado de "fixture.detectChanges ()" Parece que esse método é um ouvinte de eventos (assíncrono) que responde apenas a um retorno de chamada quando alterações são detectadas. Se nenhuma alteração for detectada, ele não chamará o retorno de chamada, resultando em um erro de tempo limite. Espero que isto ajude :)


2

Funciona após remover a scopereferência e os argumentos da função:

"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

Conforme observado por @mastablasta, mas também para adicionar que, se você chamar o argumento 'concluído' ou melhor, nomeá-lo como concluído, basta chamar o retorno de chamada como concluído () em seu teste quando terminar.

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

Manter isso no bloco resolveu meu problema.

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

0

O que fiz foi: Adicionado / atualizado o seguinte código:

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

3
Explique por que esse código funciona, em vez de apenas publicá-lo sem uma explicação.
Kobe

Então, basicamente, quando você executa um teste e leva mais tempo do que o esperado, ele falha porque o tempo limite padrão foi atingido e o script não avançou na execução. Isso pode acontecer devido a algumas condições não atendidas (por exemplo, visibilidade, carregamento da página). Agora, se o tempo limite padrão for de 1000ms >>, os scripts falharão com frequência porque são apenas um segundo e pode haver vários fatores a serem adicionados à falha do seu script. No entanto, aumentar o intervalo de tempo limite pode fazer com que o navegador / driver aguarde mais tempo para que as condições sejam atendidas.
Zeeshan

2
Ok, agora escreva isso no seu post; você deve tentar evitar apenas responder com código sem explicação :)
Kobe

0

Ao invés de

beforeEach(() => {..

usar

beforeEach(fakeAsync(() => {..

0

Parece que o teste está aguardando um retorno de chamada que nunca chega. É provável que o teste não seja executado com comportamento assíncrono.

Primeiro, veja se apenas usando fakeAsync no seu cenário "it":

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

Você também pode usar flush()para aguardar o término da fila da microTask ou tick()para aguardar um período especificado.


-2

Se você tem um argumento ( done) na itfunção, tente removê-lo também, é chamado dentro da própria função:

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
Sem nenhuma explicação de por que essa resposta não é tão útil.
Gary

2
esta resposta resolveu meu problema ... o teste angular é um pesadelo!
Benjamin Caure
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.