我从单元测试开始,很难测试使用firebase auth登录的方法。正如你在代码注释中看到的,我已经用很多方法尝试过,并用几种方法进行了测试,在大多数情况下,它总是返回:
TypeError: Cannot read property 'GoogleAuthProvider' of undefined
我使用ng mocks库来模拟AngularFireAuth。
该项目可以在github分支上的该项目分支中看到
这是我想要测试的方法:
login(): Observable<IUser> {
const provider = new firebase.auth.GoogleAuthProvider();
const responseAuth = from(this.fireAuthService.signInWithPopup(provider)).pipe(
map((fireUser) => {
const objUser: IUser = {
uid: fireUser.user?.uid!,
name: fireUser.user?.displayName!,
email: fireUser.user?.email!,
emailVerified: fireUser.user?.emailVerified!,
photoURL: fireUser.user?.photoURL!,
provider: fireUser.user?.providerId!,
};
return objUser;
}),
catchError(() => {
throw new Error('error in the logout process');
})
);
return responseAuth;
}
这是测试。
it('should executed login method with success', fakeAsync(() => {
const googleSignInMethod = spyOn(fireAuthService, 'signInWithPopup');
service.login().subscribe((data) => {
expect(googleSignInMethod).toHaveBeenCalled();
});
// const googleSignInMethod = spyOn(fireAuthService, 'signInWithPopup').and.callThrough();
// service.login().subscribe((data) => {});
// expect(googleSignInMethod).toHaveBeenCalled();
// service.login().subscribe((data) => {
// let spy = spyOn(fireAuthService, 'signInWithPopup');
// expect(spy).toHaveBeenCalledWith(provider);
// expect(spyOn(fireAuthService, 'signInWithPopup')).toBeTruthy();
// });
// spy = spyOn(service, 'isAuthenticated').and.returnValue(false); (3)
// expect(component.needsLogin()).toBeTruthy();
// expect(service.isAuthenticated).toHaveBeenCalled(); (4)
}));
测试的问题是,在测试环境中,new firebase.auth.GoogleAuthProvider();
由于0个现有应用程序而失败。
有两种解决方案如何修复它:
- 提供测试应用程序
- 避免在方法中使用
new
,并使依赖项被注入
第二种方式是首选方式:
export const FIREBASE_AUTH_PROVIDER = new InjectionToken('FIREBASE_AUTH_PROVIDER', {
providedIn: 'root',
factory: () => new firebase.auth.GoogleAuthProvider(),
});
@Injectable({
providedIn: 'root',
})
export class AuthService {
constructor(
private fireAuthService: AngularFireAuth,
@Inject(FIREBASE_AUTH_PROVIDER) private readonly fireAuthProvider: firebase.auth.AuthProvider,
) {}
login(): Observable<IUser> {
const responseAuth = from(this.fireAuthService.signInWithPopup(this.fireAuthProvider)).pipe(
// .....
然后在测试中,可以很容易地用mock:替换依赖关系
beforeEach(async () => {
await TestBed.configureTestingModule({
providers: [
AuthService,
MockProvider(AngularFireAuth),
MockProvider(FIREBASE_AUTH_PROVIDER, {}),
],
}).compileComponents();
service = TestBed.inject(AuthService);
fireAuthService = TestBed.inject(AngularFireAuth);
});
it('should executed login method with success', fakeAsync(() => {
const googleSignInMethod = spyOn(fireAuthService, 'signInWithPopup');
googleSignInMethod.and.returnValue(Promise.resolve({
credential: null,
user: null,
}));
service.login().subscribe((data) => {
expect(googleSignInMethod).toHaveBeenCalled();
});
// const googleSignInMethod = spyOn(fireAuthService, 'signInWithPopup').and.callThrough();
// service.login().subscribe((data) => {});
// expect(googleSignInMethod).toHaveBeenCalled();
// service.login().subscribe((data) => {
// let spy = spyOn(fireAuthService, 'signInWithPopup');
// expect(spy).toHaveBeenCalledWith(provider);
// expect(spyOn(fireAuthService, 'signInWithPopup')).toBeTruthy();
// });
// spy = spyOn(service, 'isAuthenticated').and.returnValue(false); (3)
// expect(component.needsLogin()).toBeTruthy();
// expect(service.isAuthenticated).toHaveBeenCalled(); (4)
}));