Получение TypeError pipe не является функцией при тестировании углового компонента с использованием наблюдаемого с декларативным подходом с использованием async - PullRequest
1 голос
/ 14 октября 2019

При выполнении тестов я получаю сообщение об ошибке «TypeError: this.dashboardService.serviceAgents $ .pipe is not function».

  • Код страницы ServiceDashboardкак показано ниже:

     import { Component } from '@angular/core';
     import { ServiceDashboardService } from '../services/service-dashboard.service';
     import { tap } from 'rxjs/operators';
     import { ServiceAgent } from '../interfaces/service-agent';
     @Component({
         selector: 'app-service-dashboard',
         templateUrl: './service-dashboard.page.html',
         styleUrls: ['./service-dashboard.page.css'],
     })
     export class ServiceDashboardPage {
         serviceAgentSlideOptions: any = {
             slidesPerView: 4
         };
         serviceAgents$ = this.dashboardService.serviceAgents$
             .pipe(
                 tap(serviceAgents => {
                     this.serviceAgentSlideOptions.slidesPerView = serviceAgents.length < 4 ? serviceAgents.length : 4;
                 })
             );
         constructor(private dashboardService: ServiceDashboardService) { }
     }  
  • Ниже приведен код модульного теста для ServiceDashboardPage.
      import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
      import { async, ComponentFixture, TestBed } from '@angular/core/testing';
      import { ServiceDashboardPage } from './service-dashboard.page';
      import { ServiceDashboardService } from '../services/service-dashboard.service';
      import { ServiceAgent } from '../interfaces/service-agent';
      import { of } from 'rxjs';
      describe('ServiceDashboardPage', () => {
          let component: ServiceDashboardPage;
          let fixture: ComponentFixture<ServiceDashboardPage>;
          let serviceDashboardServiceSpy: ServiceDashboardService;
          beforeEach(async(() => {
              serviceDashboardServiceSpy = jasmine.createSpyObj('ServiceDashboardService', 
           ['serviceAgents$']);
              TestBed.configureTestingModule({
                  declarations: [ServiceDashboardPage],
                  schemas: [CUSTOM_ELEMENTS_SCHEMA],
                  providers: [
                      { provide: ServiceDashboardService, useValue: serviceDashboardServiceSpy }
                  ]
              })
              .compileComponents();
          }));
          beforeEach(() => {
              fixture = TestBed.createComponent(ServiceDashboardPage);
              component = fixture.componentInstance;
              fixture.detectChanges();
          });
          it('should create', async(() => {
              (serviceDashboardServiceSpy.serviceAgents$ as unknown as 
           jasmine.Spy).and.returnValue(of([] as ServiceAgent[]));
              expect(component).toBeTruthy();
          }));
      });  

1 Ответ

2 голосов
/ 15 октября 2019

Существует ряд проблем с вашим кодом, как написано. Как было отмечено в комментариях выше, вы явно хотите, чтобы Observable в сервисе, но команда:

serviceDashboardServiceSpy = jasmine.createSpyObj('ServiceDashboardService', ['serviceAgents$']);

создаст serviceAgents$ как функцию, а не как Observable.

Но только изменение, которое не сделает ваш код тестируемым, потому что вы захотите изменить значение, возвращаемое этим Observable, и протестировать, чтобы убедиться, что ваш компонент правильно реагирует на эти изменения. Для этого вам потребуется рефакторинг вашего кода. Причина рефакторинга в том, что то, как вы настраиваете свой Observable в компоненте, определяя его немедленно, означает, что очень трудно изменить значение и легко протестировать. Однако простое перемещение назначения на ngOnInit() сделает это гораздо более легко тестируемым.

Затем вам нужно переместить fixture.detectChanges() из beforeEach() в саму спецификацию (функция it()). Причина этого в том, что fixture.detectChanges() выполнит ngOnInit(), который мы только что настроили, и мы хотим более тщательно контролировать, когда это вызывается.

Наконец, вам нужно настроить что-то, чтобы высмеивать вашу службукласс - вы пытались использовать объект serviceDashboardServiceSpy для этого, но в этом случае я предпочитаю использовать фиктивный класс, а не шпионский объект. Причина этого в том, что вы определяете serviceAgents$ в реальном классе обслуживания как СОБСТВЕННОСТЬ, а не как функцию. Это делает его немного более сложным для тестирования, и установка фиктивного класса вместо шпионского объекта, по моему мнению, делает это немного легче.

Принимая во внимание все эти вещи, я настроил StackBlitz , чтобы показать прохождение ваших тестов.

Я также добавил пару тестов, чтобы показать, как изменение значения в службе Observable изменяет связанное значение в вашем компоненте.

Вот.spec от этого StackBlitz:

class MockServiceDashboardService {
  get serviceAgents$() {
    return of({length: 2});
  }
}

describe('ServiceDashboardPage', () => {
    let component: ServiceDashboardPage;
    let fixture: ComponentFixture<ServiceDashboardPage>;

    beforeEach(async(() => {
        TestBed.configureTestingModule({
            declarations: [ServiceDashboardPage],
            schemas: [CUSTOM_ELEMENTS_SCHEMA],
            providers: [
                { provide: ServiceDashboardService, useClass: MockServiceDashboardService }
            ]
        })
        .compileComponents();
    }));
    beforeEach(() => {
        fixture = TestBed.createComponent(ServiceDashboardPage);
        component = fixture.componentInstance;
    });
    it('should create', () => {
      fixture.detectChanges();
      expect(component).toBeTruthy();
    });
    it('should have length of 2', () => {
      fixture.detectChanges();
      expect(component.serviceAgentSlideOptions.slidesPerView).toEqual(2);
    });
    it('should have a length of 3', () => {
      let dService = TestBed.get(ServiceDashboardService);
      spyOnProperty(dService, 'serviceAgents$').and.returnValue(of({length: 3}))
      fixture.detectChanges();
      expect(component.serviceAgentSlideOptions.slidesPerView).toEqual(3);
    });
}); 
...