如何在Jasmine测试中Mock getFirestore()?Firebase初始化报错解决
问题
在Jasmine单元测试中遇到错误:
FirebaseError: Firebase: No Firebase App '[DEFAULT]' has been created - call Firebase App.initializeApp() (app/no-app)
经排查,错误由UserService中的db = getFirestore()代码触发。以下是相关代码及依赖版本:
UserService代码
import { Injectable } from '@angular/core'; import { getFirestore, getDoc } from 'firebase/firestore'; import { doc, DocumentData, DocumentSnapshot, setDoc } from '@angular/fire/firestore'; import { AlertController } from '@ionic/angular'; import { IUserInfo } from '../models/userInfo'; @Injectable({ providedIn: 'root' }) export class UserService { db = getFirestore(); constructor(private alertController: AlertController) { } saveProfile(uid: string, email: string, displayName: string): Promise<void> { return setDoc(doc(this.db, 'users', uid), {uid, email, displayName}); } getProfile(uid: string): Promise<DocumentSnapshot<DocumentData>> { return getDoc(doc(this.db, `users`, uid)) } saveUserOptions(userInfo: IUserInfo, uid: string): Promise<void> { return setDoc(doc(this.db, 'users', uid), {userInfo}, { merge: true }); } async saved(message: any, header: string): Promise<void> { const alert = await this.alertController.create({ subHeader: `${header}`, message: `${message}`, buttons: [ { text: 'Ok', role: 'Ok', cssClass: 'secondary' } ] }); await alert.present(); } async getUserInfo(uid: string) { return (await this.getProfile(uid)).data(); } }
测试代码
import { TestBed } from '@angular/core/testing'; import { UserService } from './user.service'; describe('UserService', () => { let service: UserService; beforeEach(() => { TestBed.configureTestingModule({}); service = TestBed.inject(UserService); }); it('should be created', () => { expect(service).toBeTruthy(); }); });
依赖版本
"jasmine-core": "~3.8.0", "jasmine-spec-reporter": "~5.0.0", "karma": "~6.3.2", "karma-chrome-launcher": "~3.1.0", "karma-coverage": "~2.0.3", "karma-coverage-istanbul-reporter": "~3.0.2", "karma-jasmine": "~4.0.0", "karma-jasmine-html-reporter": "^1.5.0", "@angular/fire": "^7.4.1",
解决方案
要解决这个问题,需要Mock Firebase Firestore的相关函数,避免在测试中初始化真实的Firebase实例。以下是具体步骤:
1. 编写Mock测试代码
在测试文件中,Mock所有Firestore相关函数和AlertController,替换为模拟实现:
import { TestBed } from '@angular/core/testing'; import { UserService } from './user.service'; import { getFirestore } from 'firebase/firestore'; import { doc, setDoc, getDoc } from '@angular/fire/firestore'; import { AlertController } from '@ionic/angular'; // Mock Firebase Firestore核心函数 jest.mock('firebase/firestore', () => ({ getFirestore: jest.fn(() => ({ /* 模拟空的db对象 */ })) })); jest.mock('@angular/fire/firestore', () => ({ doc: jest.fn(() => ({ /* 模拟doc引用对象 */ })), setDoc: jest.fn(() => Promise.resolve()), getDoc: jest.fn(() => Promise.resolve({ data: jest.fn(() => ({ uid: 'test-uid', email: 'test@example.com', displayName: 'Test User' })) })) })); // Mock AlertController class MockAlertController { create() { return Promise.resolve({ present: jest.fn() }); } } describe('UserService', () => { let service: UserService; let mockAlertCtrl: MockAlertController; beforeEach(() => { mockAlertCtrl = new MockAlertController(); TestBed.configureTestingModule({ providers: [ UserService, { provide: AlertController, useValue: mockAlertCtrl } ] }); service = TestBed.inject(UserService); }); it('should be created', () => { expect(service).toBeTruthy(); }); // 测试saveProfile方法是否调用setDoc it('should trigger setDoc when saving profile', async () => { await service.saveProfile('test-uid', 'test@example.com', 'Test User'); expect(setDoc).toHaveBeenCalled(); }); // 测试getUserInfo是否返回正确数据 it('should return user data from getUserInfo', async () => { const result = await service.getUserInfo('test-uid'); expect(result).toEqual({ uid: 'test-uid', email: 'test@example.com', displayName: 'Test User' }); expect(getDoc).toHaveBeenCalled(); }); });
2. 优化UserService(可选)
将getFirestore()的调用移到构造函数中,或者改为依赖注入方式,让测试更灵活:
// 修改后的UserService import { Injectable } from '@angular/core'; import { Firestore, getFirestore } from 'firebase/firestore'; import { doc, DocumentData, DocumentSnapshot, setDoc } from '@angular/fire/firestore'; import { AlertController } from '@ionic/angular'; import { IUserInfo } from '../models/userInfo'; @Injectable({ providedIn: 'root' }) export class UserService { db: Firestore; constructor(private alertController: AlertController) { this.db = getFirestore(); } // 其余方法保持不变 }
这种方式下,后续测试也可以通过依赖注入直接替换Mock的Firestore实例,进一步降低耦合。
3. 注意事项
- 确保所有Firestore相关函数都被Mock,避免触发真实的Firebase调用
AlertController的create和present方法必须Mock,防止测试中弹出真实弹窗- 若使用Karma+Jasmine而非Jest,可改用
spyOn或手动创建Mock对象替代jest.mock
内容的提问来源于stack exchange,提问作者D.Hodges
相关产品推荐
相关产品推荐

