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