'Jasmine unit test Angular service with nested spies
I´m trying to test this Angular service which uses af.firestore.collection, not just af.collection, as shown below, so to be able to access the onSnapshot command.
import { Injectable } from '@angular/core';
import { AngularFirestore } from '@angular/fire/firestore';
import { Client } from '../models/client';
@Injectable({ providedIn: 'root' }) export class ClientService {
constructor(private cloud: AngularFirestore){}
Clients = this.cloud.firestore.collection('clients');
clients = [] as Client[];
selectedClient = {} as Client;
getClients(){
this.Clients.onSnapshot(clientSnapShot => {
clientSnapShot.docChanges().forEach(change => {
let client = change.doc.data() as Client;
let index = this.clients.findIndex(us => us.id === client.id);
if(change.type === 'added' && index === -1) this.clients.push(client);
if(change.type === 'modified') this.clients.splice(index, 1, client);
if(change.type === 'removed') this.clients.splice(index, 1);
});
this.clients = this.clients
.sort((a,b) => a.name > b.name ? 1 : a.name < b.name ? -1 : 0)
.sort((a,b) => a.active > b.active ? -1 : a.active < b.active ? 1 : 0)
});
}
}
When testing it with Karma, I tried to use nested jasmine.createSpyObj method. This is the spec.ts file:
import { TestBed } from "@angular/core/testing";
import { AngularFirestore, AngularFirestoreModule } from "@angular/fire/firestore";
import { of } from "rxjs";
import { ClientService } from "./client.service";
describe('ClientService', () => {
let clientServiceMock: any;
let angularFirestoreMock: any;
let fsCollecton: any;
beforeEach(() => {
clientServiceMock = jasmine.createSpyObj('ClientService', ['getClients']);
angularFirestoreMock = jasmine.createSpyObj('AngularFirestore', ['firestore', 'collection']);
fsCollecton = jasmine.createSpyObj('collection', ['onSnapshot']);
angularFirestoreMock.firestore.collection.and.returnValue(fsCollecton); // here´s the problematic line.
fsCollecton.onSnapshot.and.returnValue(of([]));
TestBed.configureTestingModule({
imports: [
AngularFirestoreModule
],
providers: [
{ provide: ClientService, useValue: clientServiceMock },
{ provide: AngularFirestore, useValue: angularFirestoreMock }
]
});
clientServiceMock = TestBed.inject(ClientService);
});
it('should be created', () => {
expect(clientServiceMock).toBeTruthy();
});
it('should call collection and onSnapshot methods', () => {
clientServiceMock.getClients();
expect(angularFirestoreMock.firestore.collection).toHaveBeenCalledTimes(1);
})
});
I know how to test fs.collection method, but when fs.firebase.collection is used, it seems Karma doesn´t recognize it, and this is the error I have:
TypeError: Cannot read properties of undefined (reading 'and')
at UserContext.<anonymous> (http://localhost:9876/_karma_webpack_/webpack:/src/app/services/client.service.spec.ts:16:47)
at ZoneDelegate.invoke (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone.js:400:1)
at ProxyZoneSpec.push.3775.ProxyZoneSpec.onInvoke (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone-testing.js:301:1)
at ZoneDelegate.invoke (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone.js:399:1)
at Zone.run (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone.js:160:1)
at runInTestZone (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone-testing.js:581:1)
at UserContext.<anonymous> (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/dist/zone-testing.js:596:1)
at <Jasmine>
which means that angularFirestoreMock.firestore.collection is not recognized. The spec.ts file was done by following the guide of Lars Bilde in https://www.youtube.com/playlist?list=PL8jcXf-CLpxolmjV5_taFP0c5LyCveDF1.
I couldn't find how to solve this in the official documentation, nor in any tutorials, and I didn't find a similar question on this site.
Any help is appreciated!
Solution 1:[1]
The problem is that you did not define a return value for angularFirestoreMock.firestore
, so angularFirestoreMock.firestore.collection
is undefined.
To fix it, add a line like angularFirestoreMock.firestore.and.returnValue(...)
.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | slim |