使用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,导致测试挂起。
问题原因与解决方法
核心原因
- 未正确Mock Firebase依赖:初始测试调用真实的
signInWithEmailAndPassword,Firebase异步操作在测试环境中可能因未初始化/无网络等原因无法完成,Promise一直处于pending状态。 - Mock路径不匹配:Mock的
logIn路径与测试文件中导入logIn的路径不一致,导致Mock未生效,实际仍调用原函数。 - 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
相关产品推荐
相关产品推荐

