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?

Mdb
fonte
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.

mastaBlasta
fonte
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

gsalgadotoledo
fonte
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_;
}));
Katana24
fonte
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();
        }));
    });
Lijo
fonte
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
      }
    }
  })
}
code.rookie
fonte
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.

Eric Soyke
fonte
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.

delliottg
fonte
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.

Anastasiia Semionova
fonte
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 () {

    }
}
Tom Van Rossom
fonte
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 :)

A. Figueroa
fonte
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} );
    });
});
Paul Sweatte
fonte
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(){
  //...
});
SoEzPz
fonte
0

jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;

Manter isso no bloco resolveu meu problema.

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

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

framework: 'jasmine',
jasmineNodeOpts: 
{
    // Jasmine default timeout
    defaultTimeoutInterval: 60000,
    expectationResultHandler(passed, assertion) 
    {
      // do something
    },
}
Zeeshan
fonte
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(() => {..
Meghnath Das
fonte
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.

Shapeshifter
fonte
-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        
});
tiagor87
fonte
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