Cannot Read Property 'doc' Of Undefined When Mocking Service Class Through Spy/karma
I am trying to test my angular application through Karma. My application is connected to a firebase firestore database. I am trying to mock a collection and use this to test my component functions.
The code snippets that I am using are the following:
sprint.service.ts:
export class SprintService {
getSprints() {
return this.firestore.collection('sprints').snapshotChanges();
}
constructor(private firestore: AngularFirestore) { }
}
sprints.component.ts
sprints : Sprint[];
constructor(private sprintService: SprintService) { }
ngOnInit() {
this.sprintService.getSprints().subscribe(data => {
this.sprints = data.map(e => {
return {
id: e.payload.doc.id, //HERE IT ERRORS
...e.payload.doc.data()
} as Sprint;
})
});
}
sprints.component.spec.ts
//Mock class
class MockSprintServce
{
getSprints(){
return of([
{id: "1", name:"TestSprint", description:"TestSprint", startDate:new Date(2000, 0, 1), endDate:new Date(2001, 0, 1), isActive:true},
{id: "2", name:"TestSprint2", description:"TestSprint2", startDate:new Date(2000, 0, 1), endDate:new Date(2001, 0, 1), isActive:false},
])
}
}
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [ FormsModule, AngularFireModule.initializeApp(environment.firebase) ],
declarations: [ ArchivedUserstoriesComponent,SprintDetailComponent, SprintsComponent, UserDetailComponent, UsersComponent, UserstoriesComponent, UserstoryDetailComponent ],
providers: [AngularFirestore, {provide: SprintService, useClass: MockSprintServce}]
})
.compileComponents();
}));
beforeEach(() => {
app.sprints = [
{id: "1", name:"TestSprint", description:"TestSprint", startDate:new Date(2000, 0, 1), endDate:new Date(2001, 0, 1), isActive:true},
{id: "2", name:"TestSprint2", description:"TestSprint2", startDate:new Date(2000, 0, 1), endDate:new Date(2001, 0, 1), isActive:false},
]
});
it(`should return all Sprints`, async(() => {
//arrange
let getSpy = spyOn(mockSprintServiceObject, 'getSprints').and.returnValue({ subscribe: () => {} });
//act
app.ngOnInit({});
//assert
expect(getSpy).toHaveBeenCalled();
expect(getSpy).toContain(app.sprints[1]);
}));
I want the code to be working without any errors. I probably think i have to rewrite the getSprints
method in my MockSprintService. Does anybody know what i should return or generate in the getSprints()
method to make my ngOnInit work again? Help would be appreciated.
Answer
I see you are importing and initialising AngularFireModule in your dynamic test module. Which means that it actually connects to firebase backend every time your run your test.. which usually is a very bad idea. What if your test cases need to test editing or removing entries? That would mean they would do it on actual data each time.
Ideally you want to mock all your dependencies and avoid importing real ones as much as possible (I know in Angular world it is not always possible).
One solution I have found for myself is to use ts-mockito library. It allows you to mock classes with ease it often it works out of the box. There's a blog post I wrote some time ago, if you'd like to learn more: Mocking with ts-mockito
...
Back to your concrete example. It looks like your mock data shape doesn't match what firebase service returns.
this.sprints = data.map(e => {
return {
id: e.payload.doc.id, //HERE IT ERRORS
...e.payload.doc.data()
} as Sprint;
})
You map every data item and it is expected to have a payload
object with doc
object, which has id
property and data()
method.
However, in your MockSprintServce, you return an observable with array of items with a shape:
{
id: "1",
name:"TestSprint",
description:"TestSprint",
startDate:new Date(2000, 0, 1),
endDate:new Date(2001, 0, 1),
isActive:true
}
Is simply doesn't match. If you want to continue with current setup and have it work, try changing the items in your
getSprints(){
return of(...
to
[{
payload: {
doc: {
id: '1',
data: () => ({id: "1", name:"TestSprint", description:"TestSprint", startDate:new Date(2000, 0, 1), endDate:new Date(2001, 0, 1), isActive:true})
}
}
}]
Related Questions
- → Make a Laravel collection into angular array (octobercms)
- → Laravel {!! Form::open() !!} doesn't work within AngularJS
- → Angularjs not working inside laravel form
- → Analysis of Flux implementations
- → how to write react component to construct HTML DOM
- → angular ng-repeat and images in a row
- → Conditional BG Color on AngularJS
- → Should I 'use strict' for every single javascript function I write?
- → getting the correct record in Angular with a json feed and passed data
- → "Undefined is not a function" at .toBe fucntion
- → angularjs data binding issue
- → Angular / JavaScript auto hydrate an instance
- → Convert generic text string in json object into valid url using Angular