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
1 Answer
Reset to default 6There 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);
});
});