NgRx 测试 - NullInjectorError:没有服务提供者

问题描述 投票:0回答:1

我正在尝试编写效果单元测试,但收到错误

NullInjectorError: No provider for StationsListService!

我的 station.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>'.

stations-list.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,但仍然不起作用。 有人知道如何解决这个问题吗?

angular unit-testing ngrx ngrx-store ngrx-effects
1个回答
1
投票

编译错误非常明显,因为您试图将类型(由

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);
    });
  });
});
© www.soinside.com 2019 - 2024. All rights reserved.