Подписка на наблюдаемое не определена только при запуске теста Angular Jasmine, но определяется при запуске самого приложения - PullRequest
0 голосов
/ 09 января 2019

Я написал модульный тест для этой функции:

getCarsAndSetup(){
    this.getCars();
    this.getFactoryInfo();
}

Это функция getCars ():

getCars() {
     const subscription = this.carDetailsService.getAll().subscribe((carDetails) => {
     this.carInfoService.setCars(carDetails);
     subscription.unsubscribe();  <-------------- Here the 
                                               subscription is undefined
                                                when running the test, 
                                                however when running
                                               the app, the subscription 
                                                is defined and
                                                  everything is fine
    });
}

Это модульный тест:

fdescribe('getCarsAndSetup', () => {
    it('should get cars and set them up', () => {
        component.getFactoriesAndUnsubscribe();
        spyOn(component, "getCars");
        spyOn(component, "getFactoryInfo");
        expect(component.getCars).toHaveBeenCalled();
        expect(component.getFactoryInfo).toHaveBeenCalled();
    });
  });

Я использую макет для carDetailsService. Это метод getAll () в макете carDetailsService:

getAll(): Observable<CarModel[]> {
    return Observable.create((observer:any) => {
        observer.next([]);
    });
}

И это тот же метод в РЕАЛЬНОМ carDetailsService:

getAll(): Observable<CarModel[]> {
    return this.http.get<CarModel[]>(this.carUrl);
}

Проблема в том, что когда я запускаю само приложение, определяется подписка в методе getCars (), я могу отписаться от него и т. Д., И все в порядке.

Однако, когда я запускаю тесты, этот тест не проходит, потому что по какой-то причине подписка не определена в функции getCars (), когда я пытаюсь отписаться от нее.

В чем может быть причина того, что подписка не определена только при запуске теста? Может быть, это как-то связано с тем, как я высмеивал функцию getAll () carDetailsService?

Ответы [ 2 ]

0 голосов
/ 09 января 2019

Хотя ответ Мартина избавился от ошибки, он помог мне обнаружить настоящую проблему, которая смехотворно глупа. Я установил шпионов ПОСЛЕ фактического вызова функции:

fdescribe('getCarsAndSetup', () => {
    it('should get cars and set them up', () => {
        component.getFactoriesAndUnsubscribe();
        spyOn(component, "getCars");
        spyOn(component, "getFactoryInfo");
        expect(component.getCars).toHaveBeenCalled();
        expect(component.getFactoryInfo).toHaveBeenCalled();
    });
  });

Когда шпионы должны были быть определены ДО фактического вызова функции:

fdescribe('getCarsAndSetup', () => {
    it('should get cars and set them up', () => {
        spyOn(component, "getCars");
        spyOn(component, "getFactoryInfo");
        component.getFactoriesAndUnsubscribe();
        expect(component.getCars).toHaveBeenCalled();
        expect(component.getFactoryInfo).toHaveBeenCalled();
    });
  });

Мне плохо, что Мартин потратил так много времени на этот ответ и прочитал длинное описание, которое я опубликовал, и оказалось, что вся проблема была лишь небольшим упущением. Но это то, что есть.

0 голосов
/ 09 января 2019

Проблема в том, что вы полагаетесь на синхронное / асинхронное поведение вашего источника Observable.

В вашем реальном приложении ваш this.carDetailsService.getAll() - это настоящий удаленный вызов (асинхронный), поэтому его подписка назначена на subscription, и все работает. Однако в ваших тестах тот же самый вызов, вероятно, является поддельным и, следовательно, синхронным, поэтому к моменту, когда вы хотите вызвать subscription.unsubscribe(), он все еще undefined (метод subscribe все еще выполняется, и подписка еще не возвращена).

Самое простое, что вы можете сделать - вместо этого передать функцию стрелки в subscribe, используя ключевое слово function. RxJS связывает this в обработчиках подписчика со своим внутренним объектом подписки (я знаю, что это немного хитрый подход, но он предназначен для использования таким образом).

const that = this;
this.carDetailsService.getAll().subscribe(function(carDetails) { // note the `function` keyword
  that.carInfoService.setCars(carDetails);
  this.unsubscribe();
});

Другим методом может быть использование takeUntil с Субъектом и завершение его внутри вашего subscribe.

Это поведение может измениться в будущем: https://github.com/ReactiveX/rxjs/issues/3983

Та же проблема в другом случае использования: RxJs: вычисление наблюдаемой длины массива в компоненте

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