Тестирование асинхронного кода

Тестирование асинхронного кода в Angular требует понимания того, как работают Promises, Observables и таймеры. Основная цель — корректно ожидать завершение асинхронной операции перед проверкой результатов.

Работа с async и fakeAsync

  • async/await Позволяет писать тесты, которые выглядят синхронно, но ожидают завершения асинхронных операций:

    it('должен получить данные', async () => {
      const data = await service.getData().toPromise();
      expect(data).toBeTruthy();
    });
  • fakeAsync и tick Используется для имитации времени и управления таймерами внутри тестов:

    import { fakeAsync, tick } from '@angular/core/testing';
    
    it('должен работать с таймером', fakeAsync(() => {
      let value = false;
      setTimeout(() => value = true, 1000);
      tick(1000);
      expect(value).toBeTrue();
    }));

    Метод tick() позволяет мгновенно продвинуть виртуальное время на заданное количество миллисекунд, что исключает задержки во время выполнения тестов.

Тестирование Observables

Для Observables часто используют оператор subscribe в сочетании с done или fakeAsync:

it('должен получать значения из Observable', (done) => {
  service.getObservableData().subscribe(value => {
    expect(value).toBe(42);
    done();
  });
});

Использование done гарантирует, что тест завершится только после получения данных.

Контроль асинхронных операций с TestScheduler

TestScheduler из RxJS позволяет точно управлять временем эмиссий событий:

import { TestScheduler } from 'rxjs/testing';
import { of } from 'rxjs';
import { delay } from 'rxjs/operators';

const scheduler = new TestScheduler((actual, expected) => {
  expect(actual).toEqual(expected);
});

scheduler.run(({ expectObservable }) => {
  const source$ = of('a').pipe(delay(10, scheduler));
  expectObservable(source$).toBe('10ms a');
});

Это особенно полезно для сложных потоков данных, где важно предсказуемое поведение теста без зависимости от реального времени.

Обработка ошибок и таймаутов

Асинхронные тесты должны учитывать возможные ошибки и таймауты:

it('должен обработать ошибку', async () => {
  await expectAsync(service.getErrorData()).toBeRejectedWithError('Ошибка сервера');
});

Метод expectAsync позволяет проверять правильность обработки исключений, что обеспечивает надежность приложения при нестандартных ситуациях.