Testowanie Angular 2 - wywołanie funkcji asynchronicznej - kiedy używać

86

Kiedy używasz funkcji asynchronicznej w TestBed podczas testowania w Angular 2?

Kiedy tego używasz?

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

A kiedy tego używasz?

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

Czy ktoś może mnie w tym oświecić?

xiotee
źródło

Odpowiedzi:

95

asyncnie pozwoli na rozpoczęcie następnego testu, dopóki nie asynczakończy wszystkich jego zadań. To, co asyncrobi, to zawijanie wywołania zwrotnego w strefie, w której setTimeoutśledzone są wszystkie zadania asynchroniczne (np. ). Po ukończeniu wszystkich zadań asynchronicznych następuje asynczakończenie.

Jeśli kiedykolwiek pracowałeś z Jasmine poza Angularem, być może widziałeś done, jak przechodzisz do oddzwaniania

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

Tutaj jest to rodzima Jasmine, gdzie mówimy Jasmine, że ten test powinien opóźnić zakończenie do czasu, gdy zadzwonimy done(). Jeśli nie zadzwoniliśmy, done()a zamiast tego zrobiliśmy to:

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

Test zakończyłby się jeszcze przed oczekiwaniem, ponieważ obietnica rozwiązuje się po zakończeniu testu podczas wykonywania zadań synchronicznych.

Z Angular (w środowisku Jasmine), Angular będzie w rzeczywistości wywoływał doneza kulisami, gdy używamy async. Będzie śledził wszystkie asynchroniczne zadania w Strefie, a gdy wszystkie zostaną zakończone, donezostaną wywołane za kulisami.

W twoim konkretnym przypadku z TestBedkonfiguracją, użyjesz tego ogólnie, kiedy chcesz compileComponents. Rzadko spotykam się z sytuacją, w której musiałbym inaczej to nazwać

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

Podczas testowania komponentu, który używa templateUrl(jeśli nie używasz webpacka), Angular musi wysłać żądanie XHR, aby pobrać szablon, więc kompilacja komponentu byłaby asynchroniczna. Dlatego przed kontynuowaniem testów powinniśmy poczekać, aż problem zniknie.

Paul Samsotha
źródło
Świetna odpowiedź @peeskillet. Żeby się upewnić, że to rozumiem: jeśli masz szablon wbudowany, asyncnie jest to konieczne. Kiedy używasz templateUrl, tak jest. Jednak uwzględnienie asyncnie spowoduje „zerwania” komponentu szablonu wbudowanego. Czy uważasz, że można bezpiecznie powiedzieć, że można po prostu domyślnie używać asyncdo każdego testu?
vince
2
@vincecampanale TemplateUrl ma znaczenie tylko podczas konfiguracji w beforeEach. W takim przypadku musisz zadzwonić compileComponents. Nie ma to nic wspólnego z używaniem asyncw każdym teście, jeśli o to pytasz. Jeśli chodzi o bezpieczeństwo (kiedy powinieneś zadzwonić compileComponents), zobacz Kiedy mam zadzwonić do compileComponents
Paul Samsotha
2
@vincecampanale Nie zawsze jest tak, że chcesz go wywołać przed testem. Czasami możesz chcieć to wywołać po wykonaniu jakiejś inicjalizacji. Musisz zrozumieć, co tak naprawdę robi wywołanie. Jednak w większości przypadków powinno być OK. Ale ja osobiście nie podoba mi się, że podjęli tę decyzję na własną rękę. Ale widzę, że wiele osób napotyka problem, kiedy zapominają go nazwać i zastanawiają się, dlaczego coś nie działa. Może więc lepiej, żeby wygenerowali połączenie. Lokalizacja może być dyskusyjna, ale przynajmniej ją nazywają
Paul Samsotha
2
@vincecampanale Generalnie, gdy chcesz, aby widok (ponownie) był renderowany, powinieneś go wywołać. Na przykład Utwórz komponent -> renderuj widok. Ale jeśli chcesz najpierw zainicjować coś takiego jak Utwórz komponent -> zmień wartość w komponencie, który jest używany do renderowania -> renderuj widok. To właśnie mam na myśli, mówiąc, że może chcesz najpierw coś zainicjować
Paul Samsotha
1
Aha i jeszcze jedno. Pierwsze wywołanie ma miejsce, gdy ngOnInitwywoływany jest komponent. Czasami ma to znaczenie podczas testów
Paul Samsotha
26

Gdy wykonujesz wywołanie asynchroniczne w teście, rzeczywista funkcja testowa zostanie zakończona przed zakończeniem wywołania asynchronicznego. Gdy trzeba zweryfikować stan zakończenia wywołania (co zwykle ma miejsce), platforma testowa zgłosi test jako zakończony, podczas gdy nadal trwają prace asynchroniczne.

Używając, async(...)możesz powiedzieć platformie testowej, aby poczekała, aż obietnica zwrotu lub obserwowalna zostanie zakończona, zanim potraktujesz test jako zakończony.

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

Kod przekazany do then(...)zostanie wykonany po zakończeniu samej funkcji testowej. Dzięki temu async()środowisko testowe będzie świadome, że musi czekać na wypełnienie obietnic i obserwacji, zanim potraktuje test jako zakończony.

Zobacz też

Günter Zöchbauer
źródło