You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 15:26:07