Angular 2 Testing - Async-Funktionsaufruf - wann zu verwenden

85

Wann verwenden Sie die asynchrone Funktion im TestBed beim Testen in Angular 2?

Wann benutzt du das?

 beforeEach(() => {
        TestBed.configureTestingModule({
            declarations: [MyModule],
            schemas: [NO_ERRORS_SCHEMA],
        });
    });

Und wann benutzt du das?

beforeEach(async(() => {
    TestBed.configureTestingModule({
        declarations: [MyModule],
        schemas: [NO_ERRORS_SCHEMA],
    });
}));

Kann mich jemand darüber aufklären?

xiotee
quelle

Antworten:

95

asyncDer nächste Test kann erst gestartet werden, wenn asyncalle Aufgaben abgeschlossen sind. Was asynctut , ist den Rückruf in einer Zone wickeln, wo alle asynchronen Aufgaben (zB setTimeout) verfolgt werden. Sobald alle asynchronen Aufgaben abgeschlossen sind, ist die Aufgabe asyncabgeschlossen.

Wenn Sie jemals mit Jasmine außerhalb von Angular gearbeitet haben, haben Sie möglicherweise gesehen, donedass sie an den Rückruf weitergeleitet wurden

it('..', function(done) {
  someAsyncAction().then(() => {
    expect(something).toBe(something);
    done();
  });
});

Hier ist dies einheimischer Jasmin, wo wir Jasmine sagen, dass dieser Test den Abschluss verzögern sollte, bis wir anrufen done(). Wenn wir nicht angerufen haben done()und stattdessen Folgendes getan haben:

it('..', function() {
  someAsyncAction().then(() => {
    expect(something).toBe(something);
  });
});

Der Test würde noch vor der Erwartung abgeschlossen sein, da das Versprechen nach Abschluss des Tests zur Ausführung der synchronen Aufgaben aufgelöst wird.

Mit Angular (in einer Jasminumgebung) ruft Angular tatsächlich donehinter die Kulissen, wenn wir verwenden async. Es verfolgt alle asynchronen Aufgaben in der Zone und wird, wenn alle erledigt sind, donehinter den Kulissen aufgerufen.

In Ihrem speziellen Fall mit der TestBedKonfiguration würden Sie dies im Allgemeinen verwenden, wenn Sie möchten compileComponents. Ich stoße selten auf eine Situation, in der ich es anders nennen müsste

beforeEach(async(() => {
   TestBed.configureTestingModule({
     declarations: [MyModule],
     schemas: [NO_ERRORS_SCHEMA],
   })
   .compileComponent().then(() => {
      fixture = TestBed.createComponent(TestComponent);
   });
}));

Wenn Sie eine Komponente testen, die verwendet templateUrl(wenn Sie kein Webpack verwenden), muss Angular eine XHR-Anforderung stellen, um die Vorlage abzurufen, damit die Kompilierung der Komponente asynchron ist. Wir sollten also warten, bis es behoben ist, bevor wir mit dem Testen fortfahren.

Paul Samsotha
quelle
Tolle Antwort @peeskillet. Nur um sicherzugehen, dass ich es verstehe: Wenn Sie eine Inline-Vorlage haben, asyncist dies nicht erforderlich. Wenn Sie verwenden templateUrl, ist es. Durch das Einschließen asyncwird eine Inline-Vorlagenkomponente jedoch nicht "beschädigt". Glaubst du, man kann mit Sicherheit sagen, dass man standardmäßig asyncfür jeden Test verwenden kann?
Vince
2
@vincecampanale Die templateUrl ist nur während der Konfiguration in beforeEach von Bedeutung. In diesem Fall müssen Sie anrufen compileComponents. Es hat nichts mit der Verwendung asyncbei jedem Test zu tun, wenn Sie danach fragen. Was die Sicherheit betrifft (wann Sie anrufen sollten compileComponents), lesen
Paul Samsotha
2
@vincecampanale Es ist nicht immer so, dass Sie möchten, dass es vor dem Test aufgerufen wird. Manchmal möchten Sie es nach einer Initialisierung aufrufen. Sie müssen verstehen, was das Aufrufen tatsächlich bewirkt. Meistens sollte es aber OK sein. Aber ich persönlich mag es nicht, dass sie es auf sich genommen haben, diese Entscheidung zu treffen. Aber ich sehe viele Leute, die auf das Problem stoßen, wo sie vergessen, es zu nennen, und sie fragen sich, warum etwas nicht funktioniert. Vielleicht ist es besser, wenn sie den Anruf generieren. Der Ort mag umstritten sein, aber zumindest nennen sie es
Paul Samsotha
2
@vincecampanale Wenn Sie die Ansicht (neu) rendern möchten, sollten Sie sie im Allgemeinen aufrufen. Zum Beispiel Komponente erstellen -> Ansicht rendern. Wenn Sie jedoch zuerst etwas initialisieren möchten, z. B. Komponente erstellen -> Wert in der Komponente ändern, die zum Rendern verwendet wird -> Ansicht rendern. Das meine ich damit, dass Sie vielleicht zuerst etwas initialisieren möchten
Paul Samsotha
1
Oh und noch eine Sache. Das erste Mal, wenn Sie es aufrufen, ist, wenn ngOnInitin der Komponente aufgerufen wird. Manchmal ist dies beim Testen
wichtig
26

Wenn Sie in Ihrem Test einen asynchronen Aufruf tätigen, wird die eigentliche Testfunktion abgeschlossen, bevor der asynchrone Aufruf abgeschlossen ist. Wenn Sie einen Status überprüfen müssen, in dem der Anruf abgeschlossen wurde (was normalerweise der Fall ist), meldet das Testframework den Test als abgeschlossen, während noch asynchrone Arbeiten ausgeführt werden.

Wenn async(...)Sie verwenden, weisen Sie das Test-Framework an, zu warten, bis das Rückgabeversprechen oder die beobachtbare Zahl erfüllt ist, bevor Sie den Test als abgeschlossen behandeln.

it('should show quote after getQuote promise (async)', async(() => {
  fixture.detectChanges();

  fixture.whenStable().then(() => { // wait for async getQuote
    fixture.detectChanges();        // update view with quote
    expect(el.textContent).toBe(testQuote);
  });
}));

Der Code wurde an übergeben then(...) wird ausgeführt, nachdem die Testfunktion selbst abgeschlossen wurde. Wenn async()Sie das Test-Framework darauf aufmerksam machen, dass es warten muss, bis Versprechen und Observablen abgeschlossen sind, bevor der Test als abgeschlossen behandelt wird.

Siehe auch

Günter Zöchbauer
quelle