NgRx测试-NullInjectorError:无服务提供者

问题描述

我正在尝试为效果编写单元测试,但出现错误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>'.

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

我尝试将服务作为SpyObj注入,例如stationsListService = Testbed.inject(StationsListService) as jasmine.SpyObj<StationsListService>;,但仍然无法正常工作。 有人有任何想法如何解决这个问题吗? 预先谢谢你!

解决方法

当您试图为不兼容的类型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);
    });
  });
});