2017-07-15 124 views
2

我一直在试图单元测试一个服务(AuthService),取决于AngularFireAuth单元测试使用AngularFireAuth和模拟的服务authState

我正试图找到一种方法来模拟或撬开Observable AngularFireAuth.authState,而不是实际与Firebase交谈的服务。

这里是我的测试规范:

import { inject, TestBed } 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: firebase.User = null; 
const mockAngularFireAuth: any = { authState: Observable.of(authState) }; 

describe('AuthService',() => { 
    beforeEach(() => { 
    TestBed.configureTestingModule({ 
     imports: [AngularFireModule.initializeApp(environment.firebaseAppConfig)], 
     providers: [ 
     { provider: AngularFireAuth, useValue: mockAngularFireAuth }, 
     AuthService 
     ] 
    }); 
    }); 

    it('should be defined', inject([ AuthService ], (service: AuthService) => { 
    expect(service).toBeDefined(); 
    })); 

    it('.authState should be null', inject([ AuthService ], (service: AuthService) => { 
    expect(service.authState).toBe(null); 
    })); 
}); 

这里是我(简化)服务:

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; 
     } 
    }, (error) => { 
     throw new Error(error.message); 
    }); 
    } 

    public get currentUser(): firebase.User { 
    return this.authState ? this.authState : undefined; 
    } 

    public get currentUserObservable(): Observable<firebase.User> { 
    return this.afAuth.authState; 
    } 

    public get currentUid(): string { 
    return this.authState ? this.authState.uid : undefined; 
    } 

    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(); 
    } 
} 

我得到的错误Property 'authState' is private and only accessible within class 'AuthService'.

当然是,但我不我不想实际访问它 - 我想模拟或劫持它,所以我可以从我的测试规范中控制它的价值。我相信我的代码在这里偏离我的代码。

请注意我正在使用AngularFire2的版本^4,并且引入了重大更改;这里记录:https://github.com/angular/angularfire2/blob/master/docs/version-4-upgrade.md

回答

2

封装的成员可以被反映。

难的方法:

expect(Reflect.get(service, 'authState')).toBe(null); 

最简单的办法:

expect(service['authState']).toBe(null); 
expect((service as any).authState).toBe(null); 
+0

谢谢。那我怎么能设置'authState'呢?假设我想测试Service中的currentUid getter。欢呼声 –

+1

不客气。同样,使用'Reflect.set(...)'。甚至更简单,'service ['authState'] = ...'或'(service as any).authState = ...'。它看起来像纯粹的TS问题,我会建议将'typescript'标记添加到问题而不是'firebase',因为它在这里是相关的。 – estus

+0

太棒了!只是为了教育自己;即使我不再用模拟重写'AngularFireAuth'(即不再执行'{provider:AngularFireAuth,useValue:mockAngularFireAuth}'),那么怎么没有调用Firebase?服务中的'init'应该创建一个匿名用户。这不是(在测试过程中 - 它运行应用程序时),这很好,但为什么不呢? –