Ошибка при модульном тесте - Ошибка типа: не удалось прочитать свойство 'cityWeather' из неопределенного - PullRequest
0 голосов
/ 28 мая 2019

In Component.ts,

this.dataService.currentCity.subscribe(info => {
  this.cities = info;
});

this.dataService.currentSelectedCity.subscribe(index => {
  this.selectedCityIndex = index;
});

this.selectedCityInfo = this.cities[this.selectedCityIndex];
this.selectedCityWeatherList = this.selectedCityInfo.cityWeather.list;

Я пытаюсь протестировать этот кусок кода в ngOnInit(). Но я не уверен, как проверить этот код. Здесь другой мой компонент отправляет мне массив объектов, а также индекс выбранного элемента, используя поведение, на которое я подписываюсь в `ngOnInit () этого компонента. И пытается получить доступ к свойству cityWeather в выбранном объекте.

В Spec.ts я пробовал что-то подобное, что, я уверен, не является правильным способом проверки этого.

it("should handle ngOnInit", async(() => {
        let response;
        const dataService = fixture.debugElement.injector.get(DataService);  
        spyOn(dataService, 'currentCity').and.returnValue(of(response));
        component.ngOnInit();
        expect(component.cities).toEqual(response);
    }));

Когда я запускаю приложение, я получаю вывод, но когда я тестирую функцию ngoninit, я не получаю ответ от подписки. Я получаю ошибку ниже. Failed: Cannot read property 'cityWeather' of undefined TypeError: Cannot read property 'cityWeather' of undefined

В data.service.ts

private citySubject = new BehaviorSubject<any>('');
currentCity = this.citySubject.asObservable();
private selectedCity = new BehaviorSubject<any>('');
currentSelectedCity= this.selectedCitySubject.asObservable();

sendCityWeatherInfo(info,index) {
  this.citySubject.next(info);
  this.selectedCitySubject.next(index);
}

Я вызываю это в подобном компоненте (передавая массив объектов и индекс)

this.dataService.sendCityWeatherInfo(this.cities, index);

Ответы [ 2 ]

0 голосов
/ 28 мая 2019

Поскольку вы имеете дело с наблюдаемыми потоками, которые по своей природе асинхронны, this.cities и this.selectedCityIndex могут обновляться в неизвестном порядке или в неизвестное время.

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

this.selectedCityInfo = this.cities[this.selectedCityIndex];
this.selectedCityWeatherList = this.selectedCityInfo.cityWeather.list;

Например, при попытке назначить this.selectedCityInfo, this.cities или this.selectedCityIndex может быть неопределено, и назначение завершится неудачно. Более того, в вашем сервисе вы инициализируете обоих ваших субъектов поведения пустыми строками .. которые имеют другой тип данных.

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

Вместо этого, я бы предложил объединить обе наблюдаемые в одной подписке с помощью функции CombineLatest или рефакторинг битов следующим образом:

Поскольку в вашем сервисе updateSelectedCity, который вы излучаете из обеих наблюдаемых одновременно, возможно, (если это соответствует вашему приложению) будет проще испустить оба значения в одной наблюдаемой? Тогда мы бы знали, что оба значения определенно поступили, и мы можем сделать назначения this.selectedCityInfo и this.selectedCityWeatherList в функции обратного вызова подписки.

В службе:

/* you can keep private property and recast it to Observable,
but you can also subscribe to Subjects and BehaviorSubjects directly
since they extend Observable class.
So that's what I'm doing here */

cityWeatherInfo$ =  = new BehaviorSubject<any>({ info: null, index: null });

sendCityWeatherInfo(info,index) {
  this.cityWeatherInfo$.next({ info, index });

}

В компоненте:

this.dataService.cityWeatherInfo$
  .subscribe(({ info, index }) => {
    if (info !== null && index !== null) {
      this.cities = info;
      this.selectedCityIndex = index;
      this.selectedCityInfo = this.cities[this.selectedCityIndex];
      this.selectedCityWeatherList = this.selectedCityInfo.cityWeather.list;
    }    
  });

P.S. Я не знаю всех требований вашего приложения, но, возможно, вы можете использовать в своем сервисе простой Subject вместо BehaviorSubject. Таким образом, он не будет выдавать начальные нулевые значения, и проверка if (info !== null && index !== null) не потребуется.

0 голосов
/ 28 мая 2019

Ошибка не в вашем тесте, она в этой строке:

this.selectedCityInfo = this.cities[this.selectedCityIndex];

this.cities и this.selectedCityIndex еще не определены - вы подписались на dataService, который обновит их, но служба не запустилась. Кроме того, при запуске службы this.selectedCityInfo обновляться не будет.

Вместо этого обновляйте эти значения всякий раз, когда вы получаете новые данные. Простое решение:

  this.dataService.currentCity.subscribe(info => {
    this.cities = info;
    this.updateSelectedCity();
  });

  this.dataService.currentSelectedCity.subscribe(index => {
    this.selectedCityIndex = index;
    this.updateSelectedCity();
  });
}

...

updateSelectedCity() {
  this.selectedCityInfo = this.cities[this.selectedCityIndex];
  this.selectedCityWeatherList = this.selectedCityInfo.cityWeather.list;
}

Для тестирования this.selectedCityInfo и this.selectedCityWeatherList не тестируются. Я не знаком с библиотекой тестирования, но вы, вероятно, хотите что-то вроде expect(component.selectedCityInfo).toEqual(...), то же самое с this.selectedCityWeatherList.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...