I have an authService that, when instantiated, subscribes to the AngularFireAuth Observable authState and sets the internal (personal) property of the authState service.
So, I can unit test authService I highly appreciate the internal authState of the Reflect.get/set services in my test specifications so that I can control its value.
The problem, of course, authService still subscribes to the AngularFireAuth Observable authState at the time of its creation , and I don't want and don't need it.
I suppose I need to mock AngularFireAuth, which fakes a subscription and doesn't actually tell Firebase? New for unit tests. I do not understand how to do this.
auth.service.ts
import { Injectable } from '@angular/core'; import { AngularFireAuth } from 'angularfire2/auth'; import * as firebase from 'firebase/app'; import { Observable } from 'rxjs/Rx'; @Injectable() export class AuthService { private authState: firebase.User; constructor(private afAuth: AngularFireAuth) { this.init(); } private init(): void { this.afAuth.authState.subscribe((authState) => { if (authState === null) { this.afAuth.auth.signInAnonymously() .then((authState) => { this.authState = authState; }) .catch((error) => { throw new Error(error.message); }); } else { this.authState = authState; } console.log(authState); }, (error) => { throw new Error(error.message); }); } public get currentUid(): string { return this.authState ? this.authState.uid : undefined; } public get currentUser(): firebase.User { return this.authState ? this.authState : undefined; } public get currentUserObservable(): Observable<firebase.User> { return this.afAuth.authState; } public get isAnonymous(): boolean { return this.authState ? this.authState.isAnonymous : false; } public get isAuthenticated(): boolean { return !!this.authState; } public logout(): void { this.afAuth.auth.signOut(); } }
auth.service.spec.ts
import { async, fakeAsync, inject, TestBed, tick } from '@angular/core/testing'; import { AngularFireModule } from 'angularfire2'; import { AngularFireAuth, AngularFireAuthModule } from 'angularfire2/auth'; import * as firebase from 'firebase/app'; import 'rxjs/add/observable/of'; // import { BehaviorSubject } from 'rxjs/BehaviorSubject'; import { Observable } from 'rxjs/Rx'; import { AuthService } from './auth.service'; import { environment } from '../../environments/environment'; const authState = { isAnonymous: true, uid: '17WvU2Vj58SnTz8v7EqyYYb0WRc2' } as firebase.User; describe('AuthService', () => { beforeEach(() => { TestBed.configureTestingModule({ imports: [AngularFireModule.initializeApp(environment.firebaseAppConfig)], providers: [ AngularFireAuth, AuthService ] }); }); it('should be defined', inject([ AuthService ], (service: AuthService) => { expect(service).toBeDefined(); })); it('.currentUser should be anonymous', inject([ AuthService ], (service: AuthService) => { Reflect.set(service, 'authState', authState); expect(service.currentUser).toBe(authState); })); it('.currentUser should be undefined', inject([ AuthService ], (service: AuthService) => { expect(service.currentUser).toBe(undefined); })); it('.currentUserObservable should be anonymous', inject([ AuthService ], (service: AuthService) => { Reflect.set(service, 'authState', authState); service.currentUserObservable.subscribe((value) => { expect(value).toBe(authState); }); })); it('.currentUserObservable should be undefined', inject([ AuthService ], (service: AuthService) => { service.currentUserObservable.subscribe((value) => { expect(value).toBe(undefined); }); })); it('.currentUid should be of type String', inject([ AuthService ], (service: AuthService) => { Reflect.set(service, 'authState', authState); expect(service.currentUid).toBe(authState.uid); })); it('.currentUid should be undefined', inject([ AuthService ], (service: AuthService) => { expect(service.currentUid).toBe(undefined); })); it('.isAnonymous should be false', inject([ AuthService ], (service: AuthService) => { expect(service.isAnonymous).toBe(false); })); it('.isAnonymous should be true', inject([ AuthService ], (service: AuthService) => { Reflect.set(service, 'authState', authState); expect(service.isAnonymous).toBe(true); })); });
For bonus points, two excluded tests ( .currentUserObservable should be anonymous and .currentUserObservable should be undefined ) cause an Error: 'expect' was used when there was no current spec, this could be because an asynchronous test timed out error Error: 'expect' was used when there was no current spec, this could be because an asynchronous test timed out , but only when entering console during authService creation. I wonder why it was?