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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 13:37:57