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

使用Async/await的Jest测试Firebase登录函数无法终止

问题:Jest测试logIn异步函数无法终止,Promise未resolve

问题背景

使用Jest测试调用Firebase signInWithEmailAndPassword接口的logIn异步函数时,测试无法自动终止,必须手动中断。该函数在应用中可正常运行,但测试里await之后的代码完全没执行。

待测试函数代码

export const logIn = async (email: string, password: string) => {
  return signInWithEmailAndPassword(auth, email, password)
    .then(userCredential => {
      const user = userCredential.user;
      console.log('user');
      console.log(user);
      return true;
    })
    .catch(error => {
      const errorCode = error.code;
      const errorMessage = error.message;
      console.log('Error code: ' + errorCode);
      console.log('Error message: ' + errorMessage);
      return false;
    });
};

初始测试代码(未Mock)

test('logIn should return true if user info is correct', async () => {
  let value = await logIn('noahmatwalker@gmail.com', 'password');
  expect(value).toBe(true);
});

Mock后的测试代码

jest.mock('../src/functions/helperFunctions', () => ({
  logIn: async () => {
    console.log('hello');
    return true;
  },
}));

test('testing logIn function', async () => {
    console.log('hello');
    let value = await logIn('noahmatwalker@gmail', 'password');
    value = true;
    console.log('after await');
    expect(value).toBe(true);
  });

运行后控制台仅输出hello,after await未打印,说明logIn的Promise从未resolve,导致测试挂起。


问题原因与解决方法

核心原因

  1. 未正确Mock Firebase依赖:初始测试调用真实的signInWithEmailAndPassword,Firebase异步操作在测试环境中可能因未初始化/无网络等原因无法完成,Promise一直处于pending状态。
  2. Mock路径不匹配:Mock的logIn路径与测试文件中导入logIn的路径不一致,导致Mock未生效,实际仍调用原函数。
  3. Mock写法隐患:虽然后续Mock用了async函数,但显式返回resolved Promise会更稳妥(不过此问题中主要是路径问题)。

解决步骤

方法1:正确Mock Firebase核心方法(推荐)

不Mock整个logIn,而是Mock Firebase的signInWithEmailAndPassword,以此测试logIn的真实逻辑:

import { signInWithEmailAndPassword } from 'firebase/auth';
import { logIn } from '../src/functions/helperFunctions';

// Mock Firebase的signInWithEmailAndPassword方法
jest.mock('firebase/auth', () => ({
  ...jest.requireActual('firebase/auth'), // 保留其他未Mock的方法
  signInWithEmailAndPassword: jest.fn()
}));

// 测试成功场景
test('logIn returns true with valid credentials', async () => {
  // 模拟成功返回用户凭证
  (signInWithEmailAndPassword as jest.Mock).mockResolvedValue({
    user: { uid: 'test-user-123' }
  });

  const result = await logIn('test@example.com', 'correct-pass');
  expect(result).toBe(true);
  expect(signInWithEmailAndPassword).toHaveBeenCalledWith(auth, 'test@example.com', 'correct-pass');
});

// 测试失败场景
test('logIn returns false with invalid credentials', async () => {
  // 模拟失败返回错误
  (signInWithEmailAndPassword as jest.Mock).mockRejectedValue({
    code: 'auth/wrong-password',
    message: 'Incorrect password'
  });

  const result = await logIn('test@example.com', 'wrong-pass');
  expect(result).toBe(false);
});

方法2:修正Mock路径(若需Mock整个logIn)

确保测试文件导入logIn的路径与Mock路径完全一致:

// 导入路径必须和Mock路径匹配
import { logIn } from '../src/functions/helperFunctions';

jest.mock('../src/functions/helperFunctions', () => ({
  logIn: async () => {
    console.log('hello');
    return Promise.resolve(true); // 显式返回resolved Promise
  },
}));

test('testing logIn function', async () => {
  console.log('hello');
  const value = await logIn('noahmatwalker@gmail', 'password');
  console.log('after await');
  expect(value).toBe(true);
});

方法3:初始化并清理Firebase(不推荐调用真实接口)

若必须调用真实Firebase接口,需在测试前后处理初始化与资源清理:

import { initializeApp, deleteApp, getApp } from 'firebase/app';
import { firebaseConfig } from '../src/firebase-config';

beforeAll(() => {
  // 初始化测试环境的Firebase
  initializeApp(firebaseConfig);
});

afterAll(async () => {
  // 清理Firebase连接,避免挂起
  await deleteApp(getApp());
});

内容的提问来源于stack exchange,提问作者arkbuilder117

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 21:27:29