最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Getting TypeError pipe is not a function while testing angular component using observable with declarative approach

programmeradmin7浏览0评论

When run the tests i am getting i am getting the "TypeError: this.dashboardService.serviceAgents$.pipe is not a function" error.

  • ServiceDashboard page code as below :

     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) { }
     }  
  • Following is the unit test code for the 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 ponent: 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 }
                  ]
              })
              pileComponents();
          }));
          beforeEach(() => {
              fixture = TestBed.createComponent(ServiceDashboardPage);
              ponent = fixtureponentInstance;
              fixture.detectChanges();
          });
          it('should create', async(() => {
              (serviceDashboardServiceSpy.serviceAgents$ as unknown as 
           jasmine.Spy).and.returnValue(of([] as ServiceAgent[]));
              expect(ponent).toBeTruthy();
          }));
      });  

When run the tests i am getting i am getting the "TypeError: this.dashboardService.serviceAgents$.pipe is not a function" error.

  • ServiceDashboard page code as below :

     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) { }
     }  
  • Following is the unit test code for the 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 ponent: 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 }
                  ]
              })
              .pileComponents();
          }));
          beforeEach(() => {
              fixture = TestBed.createComponent(ServiceDashboardPage);
              ponent = fixture.ponentInstance;
              fixture.detectChanges();
          });
          it('should create', async(() => {
              (serviceDashboardServiceSpy.serviceAgents$ as unknown as 
           jasmine.Spy).and.returnValue(of([] as ServiceAgent[]));
              expect(ponent).toBeTruthy();
          }));
      });  
Share Improve this question asked Oct 14, 2019 at 10:48 Kiran Kumar PKiran Kumar P 1132 silver badges8 bronze badges 5
  • because it's not a function is an Observable – Maciej Wojcik Commented Oct 14, 2019 at 10:51
  • is value updated in slidesPerView in your ponent, cause I see that you haven't subscribed to the serviceAgents$ observable. – Anand Bhushan Commented Oct 14, 2019 at 11:27
  • @AnandBhushan i am using async pipe to subscribe/unsubscribe observable automatically in ponent html code. <app-service-agent [serviceAgents]="serviceAgents$ | async"></app-service-agent> – Kiran Kumar P Commented Oct 14, 2019 at 11:40
  • try putting (serviceDashboardServiceSpy.serviceAgents$ as unknown as jasmine.Spy).and.returnValue(of([] as ServiceAgent[])); in beforeEach – Anand Bhushan Commented Oct 14, 2019 at 11:42
  • @AnandBhushan I tried but still getting same error – Kiran Kumar P Commented Oct 14, 2019 at 11:48
Add a ment  | 

1 Answer 1

Reset to default 6

There are a number of issue with your code as written. As it was pointed out in the ments above, you clearly want an Observable in the service, but the mand:

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

will create serviceAgents$ as a function, and not as an Observable.

But just changing that won't make your code testable, because you will want to change the value that is returned by that Observable and test to see that your ponent is reacting properly to those changes. For that you will need to refactor your code. The reason for the refactor is because the way you are setting up your Observable in the ponent by defining it immediately means that it is very hard to change the value and easily test. Simply moving the assignment to ngOnInit() however, will make this much more easily testable.

Then, you need to move fixture.detectChanges() out of the beforeEach() and into the spec itself (the it() function). The reason for this is because fixture.detectChanges() will execute ngOnInit() which we just set up, and we want to more carefully control when that is called.

Finally, you need to set up something to mock your service class - you were trying to use serviceDashboardServiceSpy object to do so, but in this case I prefer to use a mock class rather than a spy object. The reason for this is because you are defining serviceAgents$ within the real service class as a PROPERTY and not as a function. This makes it a little more tricky to test, and setting up a mock class instead of a spy object in my opinion makes this a little easier.

Taking all these things into account, I set up this StackBlitz to show your tests passing.

I also added a couple of tests to show how changing the value in the service Observable changes the associated value within your ponent.

Here is the .spec from that StackBlitz:

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

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

    beforeEach(async(() => {
        TestBed.configureTestingModule({
            declarations: [ServiceDashboardPage],
            schemas: [CUSTOM_ELEMENTS_SCHEMA],
            providers: [
                { provide: ServiceDashboardService, useClass: MockServiceDashboardService }
            ]
        })
        .pileComponents();
    }));
    beforeEach(() => {
        fixture = TestBed.createComponent(ServiceDashboardPage);
        ponent = fixture.ponentInstance;
    });
    it('should create', () => {
      fixture.detectChanges();
      expect(ponent).toBeTruthy();
    });
    it('should have length of 2', () => {
      fixture.detectChanges();
      expect(ponent.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(ponent.serviceAgentSlideOptions.slidesPerView).toEqual(3);
    });
}); 

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论