Тестирование NgRx — ошибка NullInjectorError: нет поставщика услуг

#angular #unit-testing #ngrx #ngrx-store #ngrx-effects

#angular #модульное тестирование #ngrx #ngrx-store #ngrx-эффекты

Вопрос:

Я пытаюсь написать модульные тесты для эффектов, и я получил ошибку NullInjectorError: No provider for StationsListService! .

Мои stations.effects.ts:

 @Injectable()
export class StationsListEffects {
  constructor(private actions$: Actions, private stationsListService: StationsListService) {}

  @Effect()
  loadStationsList$ = this.actions$.pipe(
    ofType(StationsListActionTypes.LoadStationsList),
    exhaustMap(() => this.stationsListService.getStations()),
    map((stationsList: StationListItem[]) => new StationsLoaded(stationsList)),
    catchError((error: Error) => of(new StationsLoadFailed(error)))
  );
}
  

И stations.effects.spec.ts является:

 describe('StationsListEffects', () => {
  let actions: Observable<any>;

  let effects: StationsListEffects;
  let stationsListService: jasmine.SpyObj<StationsListService>;

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        StationsListService,
        StationsListEffects,
        provideMockActions(() => actions),
        {
          provide: StationsListService,
          useValue: {
            getStations: jasmine.createSpy(),
          },
        },
      ],
    });

    effects = TestBed.inject(StationsListEffects);
    stationsListService = TestBed.inject(StationsListService);
  });

  describe('loadStationsList', () => {
    it('should return a stream with stations list loaded action', () => {
      const stationsList: StationListItem[] = [
        {
          id: '123',
          identifier: 'identifier 123',
          name: 'west',
          address: {
            city: 'sv',
            streetAndHouseNumber: 'str. Universitatii 13',
            postcode: '720234',
            state: 'sv',
          },
          active: false,
        },
      ];
      const action = new LoadStationsList();
      const outcome = new StationsLoaded(stationsList);

      actions = hot('-a', { a: action });
      const response = cold('-a|', { a: stationsList });
      stationsListService.getStations.and.returnValue(response);

      const expected = cold('--b', { b: outcome });
      expect(effects.loadStationsList$).toBeObservable(expected);
    });

    it('should fail and return an action with the error', () => {
      const action = new LoadStationsList();
      const error = new Error('some error') as any;
      const outcome = new StationsLoadFailed(error);

      actions = hot('-a', { a: action });
      const response = cold('-#|', {}, error);
      stationsListService.getStations.and.returnValue(response);

      const expected = cold('--(b|)', { b: outcome });
      expect(effects.loadStationsList$).toBeObservable(expected);
    });
  });
});
  

У stationsListService = TestBed.inject(StationsListService); меня есть ошибка, которая гласит: Type 'StationsListService' is not assignable to type 'SpyObj<StationsListService>'.

Список станций.service.ts является:

 @Injectable()
export class StationsListService {
  private stationList: StationListItem[] = [];

  public get stationsList(): StationListItem[] {
    return this.stationList;
  }

  private baseUrl = '//localhost:8080/api/stations';

  constructor(private httpClient: HttpClient) {}

  public getStations(): any {
    return this.httpClient.get<Array<StationListItem>>(this.baseUrl).pipe(
      tap((data) => {
        this.stationList = data;
      })
    );
  }

  public addStation(station: StationListItem): any {
    return of(null).pipe(delay(2000));
  }

  public updateStation(station: StationListItem): any {
    return of(null).pipe(delay(2000));
  }

  public deleteStation(id: string): any {
    return of(null).pipe(delay(2000));
  }
}
  

Я пытался внедрить сервис как stationsListService = TestBed.inject(StationsListService) as jasmine.SpyObj<StationsListService>; SpyObj, но все равно не работает.
У кого-нибудь есть ide, как это решить?
Заранее благодарю вас!

Комментарии:

1. Иногда вам просто нужно перезапустить IDE.

Ответ №1:

Ошибка компиляции совершенно очевидна, поскольку вы пытаетесь присвоить тип (return by TestBed.inject ) типу spy , который несовместим. Чтобы исправить ошибку, сначала измените тип службы, а затем используйте spyOn для отслеживания метода в службе. давайте обновим код следующим образом —

 describe('StationsListEffects', () => {
  let actions: Observable<any>;

  let effects: StationsListEffects;
  let stationsListService: StationsListService;

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        StationsListService,
        StationsListEffects,
        provideMockActions(() => actions)
      ],
    });

    effects = TestBed.inject(StationsListEffects);
    stationsListService = TestBed.inject(StationsListService);
  });

  describe('loadStationsList', () => {
    it('should return a stream with stations list loaded action', () => {
      const stationsList: StationListItem[] = [
        {
          id: '123',
          identifier: 'identifier 123',
          name: 'west',
          address: {
            city: 'sv',
            streetAndHouseNumber: 'str. Universitatii 13',
            postcode: '720234',
            state: 'sv',
          },
          active: false,
        },
      ];
      //SPY the function and return mocked data wrapped in an observable using "of" operator
      spyOn(stationsListService, 'getStations').and.returnValue(of(stationsList));
      const action = new LoadStationsList();
      const outcome = new StationsLoaded(stationsList);

      actions = cold('-a', { a: action });
      const expected = cold('--b', { b: outcome });
      expect(effects.loadStationsList$).toBeObservable(expected);
    });
  });
});