Jest模拟Firebase Auth时遇'Cannot read properties of undefined (reading 'user')'错误,无法测试注册错误状态
Jest模拟Firebase Auth时遇'Cannot read properties of undefined (reading 'user')'错误,无法测试注册错误状态
我来帮你分析并解决这个问题。首先,我们先拆解错误根源,再一步步修复测试代码。
错误根源分析
你遇到的Cannot read properties of undefined (reading 'user')错误,本质是因为createUserWithEmailAndPassword的返回值为undefined,导致你尝试读取userCredential.user时抛出异常。
为什么会出现这种情况?大概率是你的组件中从../firebase.js导入了Firebase相关方法/实例,但你在测试中仅用jest.mock("../firebase.js")做了空mock,没有为其中的createUserWithEmailAndPassword或auth实例提供具体的模拟实现,导致调用该方法时返回undefined。
另外,你的测试中同时mock了firebase/app、firebase/auth和../firebase.js,这可能导致mock冲突,进一步加剧了问题。
分步修复方案
1. 统一Firebase相关Mock,避免冲突
优先针对你组件实际导入的../firebase.js做精确mock,覆盖所有需要用到的导出项:
// 替换你之前分散的Firebase相关mock jest.mock("../firebase.js", () => ({ // 匹配组件中导入的auth实例 auth: {}, // 模拟createUserWithEmailAndPassword返回预期的用户凭证 createUserWithEmailAndPassword: jest.fn().mockResolvedValue({ user: { uid: "123", email: "test@mail.com" } }) })); // 如果组件中用到了firebase/auth的getAuth方法,补充这个mock jest.mock("firebase/auth", () => ({ getAuth: jest.fn().mockReturnValue({}) })); // fetchData的mock保持不变 jest.mock("../utilities/fetchData", () => ({ fetchData: jest.fn() }));
2. 调整测试逻辑,正确等待异步状态更新
你的测试中查询错误信息的方式有问题:screen.findByTestId是异步方法,需要配合await使用。同时,我们可以简化异步等待的逻辑:
import "@testing-library/jest-dom"; import { render, screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { MemoryRouter } from "react-router-dom"; import { fetchData } from "../utilities/fetchData"; import BusinessSignup from "../Business/BusinessSignUp"; // 填充表单的辅助函数保持不变 async function fillInForm() { await userEvent.type(screen.getByPlaceholderText(/business name/i), "test"); await userEvent.type(screen.getByPlaceholderText(/company id/i), "RF238E2"); await userEvent.type(screen.getByPlaceholderText(/first name/i), "test"); await userEvent.type(screen.getByPlaceholderText(/last name/i), "test"); await userEvent.type(screen.getByPlaceholderText(/email address/i), "test@mail.com"); await userEvent.type(screen.getByPlaceholderText(/business address/i), "Green street"); await userEvent.type(screen.getByPlaceholderText(/postcode/i), "E1 38CD"); await userEvent.type(screen.getByPlaceholderText(/phone number/i), "07889546333"); await userEvent.type(screen.getByPlaceholderText(/^password$/i), "Password123"); await userEvent.type(screen.getByPlaceholderText(/^confirm password$/i), "Password123"); await userEvent.selectOptions(screen.getByRole('combobox'), "Services"); } async function submit() { await userEvent.click(screen.getByRole('button', { name: /submit/i })); } describe('Sign up form for enterprises', () => { it('should set signUpError when no enterprise match is found', async () => { // 模拟fetchData返回与用户输入不匹配的企业数据,触发错误分支 fetchData.mockResolvedValue([ { email: "test_2@mail.com", companyID: "YI294P7" } ]); render( <MemoryRouter> <BusinessSignup /> </MemoryRouter> ); await fillInForm(); await submit(); // 等待错误消息出现在DOM中 const errorMessage = await waitFor(() => screen.findByText('No record of your company has been found. Try again.') ); // 验证错误消息存在 expect(errorMessage).toBeInTheDocument(); // 验证fetchData被调用 expect(fetchData).toHaveBeenCalledTimes(1); // 验证createUserWithEmailAndPassword被执行(我们会先创建用户再检查企业信息) const { createUserWithEmailAndPassword } = require("../firebase.js"); expect(createUserWithEmailAndPassword).toHaveBeenCalled(); }); });
3. 额外注意事项
- 确保组件中的
auth实例和Firebase方法的导入路径,与测试中的mock完全匹配; - 测试异步逻辑时,必须用
await或waitFor等待状态更新和DOM渲染完成,避免测试因异步操作未完成而失败; - 如果你不确定某个mock是否生效,可以在测试中添加
console.log或使用jest.spyOn跟踪函数调用情况,辅助定位问题。
内容来源于stack exchange
相关产品推荐
相关产品推荐

