jasmine: asynchroniczne wywołanie zwrotne nie zostało wywołane w czasie określonym przez jasmine.DEFAULT_TIMEOUT_INTERVAL

141

Mam usługę kątową o nazwie 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
    };

});

Próbuję przetestować tę usługę przy użyciu jaśminu:

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

Czytałem o asynchronicznym wsparciu w Jasmine, ale ponieważ jestem raczej nowy w testowaniu jednostkowym z javascriptem, nie mogłem go uruchomić.

Otrzymuję błąd:

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

a mój test trwa zbyt długo (około 5s).

Czy ktoś może mi pomóc, podając działający przykład mojego kodu z jakimś wyjaśnieniem?

Mdb
źródło
1
Przetwarzanie zdarzeń jest zwykle wykonywane w cyklu podsumowania. Spróbuj dodać zakres. $ Apply () do swojego testu zamiast używać wzorca testowania asynchronicznego Jasmine
Eitan Peer,
to nie działa. Dodałem zakres. $ Apply (); zaraz po wywołaniu requestNotificationChannel.deleteMessage (1, 4), ale otrzymuję ten sam błąd ...
Mdb
Ten sam błąd pojawia się, gdy testy asynchroniczne trwają dłużej, niż się Jestspodziewano - bardzo często podczas debugowania i sprawdzania zmiennych zajmuje trochę czasu.
Dan Dascalescu
Zamiast tego spróbuj użyć krótszego limitu czasu. Otrzymałem ten błąd podczas używania timeout = 5000. Zastąpiłem go 2000 i zadziałało!
Marina Riaz
1
Zostawiam to tutaj, aby pomóc komuś w moim miejscu. Wystąpił ten błąd podczas uruchamiania testów wewnątrz kontenera dockera. Testy czasami przebiegały bez żadnych problemów, ale czasami kończyły się niepowodzeniem. Pomyślałem, że to jakiś stan wyścigu, ale nie mogłem zrozumieć, dlaczego. Zdałem sobie sprawę, że miałem afterEachkrok polegający na wyczyszczeniu bazy danych (przy użyciu deleteManymetody). Dodawanie jest.setTimeout(30000);w beforeAllsposób wydaje się stałe to dla mnie - Zgaduję, ponieważ usunięcie bazy danych jest połączenie sieciowe (wewnątrz stanie), to było czasami trwa dłużej niż 3 sekundy i rzucania.
nkhil

Odpowiedzi:

231

Posiadanie argumentu w itfunkcji ( donew poniższym kodzie) spowoduje, że Jasmine podejmie próbę wywołania asynchronicznego.

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

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

Nie ma znaczenia, jak donenazywa się argument, liczy się tylko jego istnienie. Napotkałem ten problem z powodu zbyt dużej ilości kopii / makaronu.

Dokumentacja Jasmine Asynchronous Support zwraca uwagę, że argument (wymieniony donepowyżej) to wywołanie zwrotne, które można wywołać, aby powiadomić Jasmine o zakończeniu funkcji asynchronicznej. Jeśli nigdy tego nie zadzwonisz, Jasmine nigdy się nie dowie, że test został wykonany i ostatecznie przekroczy limit czasu.

mastaBlasta
źródło
3
To samo dotyczy argumentów w opisie (w kątowym, aby to zrobić, musisz wywołać wstrzyknięcie w opisie)
Narretz
@MartinBliss To jest udokumentowane, właśnie zasugerowałem edycję, aby odnieść się do dokumentacji: stackoverflow.com/suggested-edits/2434606
Vincent
39
Uwaga dla przypadkowych pracowników Google, którzy napotkają to pytanie w przyszłości: jeśli używasz Protractora i napotkasz ten problem, ta odpowiedź nie jest tym, czego szukasz - Protractor sam wywołuje wywołanie zwrotne.
Vincent,
To naprawiło mój problem i było to spowodowane tym samym cuplrit "copy / pasta"
shaikh
1
@ Vincent, jaki jest problem użytkowników Protractor, jeśli wystąpi ten błąd?
Bruno Bieri
57

Nawet w przypadku testów asynchronicznych w takich przypadkach występuje limit czasu, który można obejść, zwiększając wartość limitu czasu w celu oceny asynchronicznego wywołania zwrotnego 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();
            }
        });
    });
});

Źródło: http://jasmine.github.io/2.0/introduction.html#section-42

gsalgadotoledo
źródło
1
Wydaje się, że to nie jest „właściwy sposób”, aby to zrobić, ale po dodaniu kilku dodatkowych zer, aby mój test Selenium został uruchomiony, był to potrzebny hack.
szmergiel
Oryginalny jasmine.DEFAULT_TIMEOUT_INTERVAL to 60000 ms. Ten przykład sprawi, że będzie sześć razy krótszy.
Waltari
Masz rację, po prostu
wstawiłem
20

Ten błąd może być również spowodowany pominięciem wstrzykiwania podczas inicjowania usługi / fabryki lub cokolwiek innego. Na przykład można go wyrzucić, wykonując następujące czynności:

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

Aby to naprawić, po prostu opakuj funkcję za pomocą inject, aby poprawnie pobrać usługę:

var service;
beforeEach(inject(function(_TestService_) {
    service = _TestService_;
}));
Katana24
źródło
13
import { fakeAsync, ComponentFixture, TestBed } from '@angular/core/testing';

użyj fakeAsync

beforeEach(fakeAsync (() => {

//your code

}));



describe('Intilalize', () => {
        it('should have a defined component', fakeAsync(() => {
            createComponent();
            expect(_AddComponent.ngOnInit).toBeDefined();
        }));
    });
Lijo
źródło
6

Możesz użyć wtyczki karma-jasmine do globalnego ustawienia domyślnego limitu czasu.

Dodaj tę konfigurację w karma.conf.js

module.exports = function(config) {
  config.set({
    client: {
      jasmine: {
        timeoutInterval: 10000
      }
    }
  })
}
code.rookie
źródło
5

Ten błąd zaczął się dla mnie nieoczekiwanie, na teście, który zawsze działał. Nie mogłem znaleźć żadnych sugestii, które pomogłyby, dopóki nie zauważyłem, że mój Macbook działa wolno. Zauważyłem, że procesor był powiązany z innym procesem, który zabiłem. Błąd asynchronizacji Jasmine zniknął, a moje testy znów są w porządku.

Nie pytaj mnie dlaczego, nie wiem. Ale w moich okolicznościach wydawało się, że przyczyną był brak zasobów systemowych.

Eric Soyke
źródło
5
Prawdopodobnie, gdy twój procesor był wolny, zadanie zakończyło się przed domyślnym limitem czasu. Gdy procesor był zajęty, testowane zadanie trwało zbyt długo.
Shane
5

To bardziej spostrzeżenie niż odpowiedź, ale może pomóc innym, którzy byli tak samo sfrustrowani jak ja.

Ciągle otrzymywałem ten błąd z dwóch testów w moim pakiecie. Myślałem, że po prostu zepsułem testy z refaktoryzacją, którą robiłem, więc po wycofaniu zmian nie zadziałało, wróciłem do wcześniejszego kodu, dwa razy (dwie wersje wstecz) myśląc, że pozbędzie się błędu. To nic nie zmieniło. Wczoraj goniłem za swoim ogonem i część dzisiejszego ranka bez rozwiązania problemu.

Byłem sfrustrowany i dziś rano sprawdziłem kod na laptopie. Uruchomiłem cały zestaw testów (około 180 testów), bez błędów. Więc błędów nigdy nie było w kodzie ani testach. Wróciłem do mojego dev boxa i zrestartowałem go, aby wyczyścić wszystko z pamięci, co mogło powodować problem. Bez zmian, te same błędy w tych samych dwóch testach. Więc usunąłem katalog z mojego komputera i sprawdziłem go ponownie. Voila! Bez błędów.

Nie mam pojęcia, co to spowodowało lub jak to naprawić, ale usunięcie katalogu roboczego i sprawdzenie go z powrotem naprawiło cokolwiek to było.

Mam nadzieję, że to komuś pomoże.

delliottg
źródło
1
Dziękuję człowieku, oszalałem na punkcie tego.
Zrestartowałem
W moim przypadku po prostu ponownie uruchomiłem polecenie i rozwiązało ten problem. Miałem ponowne ładowanie na gorąco do testów jednostkowych i za każdym razem kończyło się niepowodzeniem. Musiałem zatrzymać się i ponownie uruchomić polecenie.
jignesh
4

Nie używaj done, po prostu pozostaw wywołanie funkcji puste.

Anastasiia Semionova
źródło
Proszę poprawić mnie, jeśli się mylę, ale jak rozumiem, po prostu kończy się zestaw testów przed samym testem i zamiast tego zostaje zrzucony komunikat o błędzie. Oznacza to, że każde potwierdzenie, które się nie powiedzie, nie przerwie testu, ponieważ zestaw testów zostanie zakończony przed uruchomieniem potwierdzenia. Może to również oznaczać (widziałem podobne zachowanie), że inny test pokazuje błąd utworzony przez ten test. Wreszcie oznacza to, że na początku wszystko wygląda dobrze, ale wraz ze wzrostem liczby testów problem będzie pojawiał się sporadycznie.
LosManos
3

Ten błąd pojawia się również, gdy oczekujesz czegoś w beforeAllfunkcji!

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

    beforeAll(function () {
        ...

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

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

    }
}
Tom Van Rossom
źródło
2

W moim przypadku ten błąd był spowodowany niewłaściwym użyciem "fixture.detectChanges ()". Wygląda na to, że ta metoda jest detektorem zdarzeń (async), który odpowie tylko na wywołanie zwrotne po wykryciu zmian. Jeśli nie zostaną wykryte żadne zmiany, nie wywoła wywołania zwrotnego, co spowoduje błąd przekroczenia limitu czasu. Mam nadzieję że to pomoże :)

A. Figueroa
źródło
2

Działa po usunięciu scopeodwołania i argumentów funkcji:

"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
źródło
0

Jak zauważył @mastablasta, ale także, aby dodać, że jeśli wywołasz argument „done”, a raczej nazwij go zakończonym , po zakończeniu wywołania wywołania zwrotnego complete () w teście.

// 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
źródło
0

jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;

Trzymanie tego w bloku rozwiązało mój problem.

it('', () => {
 jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
});
Sai Prasad
źródło
0

To, co zrobiłem, to: Dodałem / zaktualizowałem następujący kod:

framework: 'jasmine',
jasmineNodeOpts: 
{
    // Jasmine default timeout
    defaultTimeoutInterval: 60000,
    expectationResultHandler(passed, assertion) 
    {
      // do something
    },
}
Zeeshan
źródło
3
Wyjaśnij, dlaczego ten kod działa, zamiast publikować go bez wyjaśnienia.
Kobe,
Więc w zasadzie, gdy wykonujesz test i trwa dłużej niż oczekiwano, kończy się niepowodzeniem, ponieważ przekroczono domyślny limit czasu, a skrypt nie wykonał postępu. Może się tak zdarzyć z powodu niespełnienia pewnych warunków (na przykład widoczności, wczytywania strony). Teraz, jeśli domyślny limit czasu wynosi 1000 ms, to skrypty często zawodzą, ponieważ jest to tylko jedna sekunda, a niepowodzenie skryptu może mieć wiele czynników. Jednak zwiększenie limitu czasu może spowodować, że przeglądarka / sterownik będzie dłużej czekać na spełnienie warunków.
Zeeshan,
2
OK, teraz napisz to w swoim poście; powinieneś starać się unikać po prostu odpowiadania kodem bez wyjaśnienia :)
Kobe
0

Zamiast

beforeEach(() => {..

posługiwać się

beforeEach(fakeAsync(() => {..
Meghnath Das
źródło
0

Wygląda na to, że test oczekuje na oddzwonienie, które nigdy nie nadejdzie. Jest to prawdopodobne, ponieważ test nie jest wykonywany z zachowaniem asynchronicznym.

Najpierw sprawdź, czy po prostu używasz fakeAsync w scenariuszu „to”:

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

Można również użyć, flush()aby poczekać na zakończenie kolejki microTask lub tick()poczekać określony czas.

Shapeshifter
źródło
-2

Jeśli masz argument ( done) w itfunkcji, spróbuj go również usunąć, wywołanie w samej funkcji:

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
źródło
4
Bez żadnego wyjaśnienia, dlaczego ta odpowiedź nie jest tak przydatna.
Gary
2
ta odpowiedź rozwiązała mój problem ... testowanie kątowe to koszmar!
Benjamin Caure,