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

如何用React Testing Library测试含Context/Provider的EventCard组件

问题:测试依赖React Context的EventCard组件时,getUserData返回undefined

我开发了一个基于React Context的Web应用,需要测试EventCard组件:当条目属于当前用户时显示拒绝按钮。我通过AuthProvider对接后端API处理登录登出,成功登录后将用户对象存入Cookie维持会话。但测试模拟登录状态时,EventCard内调用useAuth的getUserData()返回的personId始终为undefined,而测试组件内通过jest.spyOn模拟的getUserData却能正常返回123。请问如何使用React Testing Library测试依赖React Context的组件?

测试代码

import { render, screen } from "@testing-library/react";
import { EventCard } from "../EventCard";
import { stdEventRequest } from "../../helpers/standard-request-for-tests";
import { AuthProvider, useAuth, withAuth } from "../../context/auth/AuthProvider";
import { FC } from "react";
import { CookieUniversalProvider } from "@shopify/react-cookie";

/**
 * A summary of an event should look something like this:
 * 
 * ============================================================================
 * John Doe (10:30)
 * Guitar [x] (a decline button)
 * Please let your leader know why you can't make it []  [if volunteer clicks Decline]
 * -------------------------------------------------
 * ============================================================================
 */

it('should show a decline button if current user', () => {
    const Component: FC = withAuth(() => {
      const auth = useAuth();
      jest.spyOn(auth, 'getUserData').mockReturnValue({
        personId: 123,
        ...
      });

      return (
        <CookieUniversalProvider>
          <AuthProvider>
            <EventCard request={{
              personId: 123,
              personName: 'John Doe',
              positionId: 10,
              positionName: 'Guitar',
            }} />
            personId: { auth.getUserData().personId }
          </AuthProvider>
        </CookieUniversalProvider>
      )
    });

    render(<Component />);

    const positionElement = screen.getByText(/Guitar/i);
    const button = screen.getByRole('button');
    expect(positionElement).toBeInTheDocument();
    expect(button).toBeInTheDocument();
  });

我添加了personId: { auth.getUserData().personId }验证spyOn,它能正常显示123。

EventCard组件代码

export const EventCard = ({request}: EventCardProps) => {
  const { getUserData } = useAuth();
  
  return (
    <View>
      personId: {getUserData().personId}
      {request.personId === getUserData().personId ? (<>
        <Text fontWeight={600}>
          {request.positionName}
          <AcceptDeclineButton category="decline" />
        </Text>
        </>) : (<>
        <Text fontSize='small'>
          {statusToName(request)}
        </Text>
        <Text fontSize='small'>
          {request.positionName}
        </Text>
      </>)}
    </View>
  );
}

我在组件中添加了personId: {getUserData().personId},结果返回undefined。


回答

核心问题分析

你在测试中mock的getUserData只作用于当前组件的useAuth实例,而EventCard是在AuthProvider内部重新获取的独立实例,两者毫无关联,所以EventCard里的getUserData仍然是原始实现,返回undefined。

解决方案

方案一:直接Mock useAuth钩子

使用jest.mock全局mockuseAuth钩子,让所有组件调用时都返回预设的用户数据:

import { render, screen } from "@testing-library/react";
import { EventCard } from "../EventCard";
import { AuthProvider, useAuth } from "../../context/auth/AuthProvider";
import { CookieUniversalProvider } from "@shopify/react-cookie";

// Mock useAuth钩子,保留其他原始导出
jest.mock("../../context/auth/AuthProvider", () => ({
  ...jest.requireActual("../../context/auth/AuthProvider"),
  useAuth: () => ({
    getUserData: () => ({ personId: 123 })
    // 若需要其他Auth方法,可在此添加mock实现
  })
}));

it('should show a decline button if current user', () => {
  render(
    <CookieUniversalProvider>
      <AuthProvider>
        <EventCard request={{
          personId: 123,
          personName: 'John Doe',
          positionId: 10,
          positionName: 'Guitar',
        }} />
      </AuthProvider>
    </CookieUniversalProvider>
  );

  const positionElement = screen.getByText(/Guitar/i);
  const button = screen.getByRole('button');
  expect(positionElement).toBeInTheDocument();
  expect(button).toBeInTheDocument();
});

方案二:创建测试专用AuthProvider

如果需要灵活控制不同测试场景的用户状态,可以封装一个测试用的Provider,预先注入模拟数据:

import { render, screen } from "@testing-library/react";
import { EventCard } from "../EventCard";
import { AuthProvider } from "../../context/auth/AuthProvider";
import { CookieUniversalProvider } from "@shopify/react-cookie";

// 测试用AuthProvider,接收自定义userData
const TestAuthProvider = ({ children, userData }) => {
  const mockAuthValue = {
    getUserData: () => userData,
    // 按需添加其他需要mock的Auth方法
  };

  // 假设AuthProvider支持传入value覆盖默认Context值
  return (
    <AuthProvider value={mockAuthValue}>
      {children}
    </AuthProvider>
  );
};

it('should show a decline button if current user', () => {
  render(
    <CookieUniversalProvider>
      <TestAuthProvider userData={{ personId: 123 }}>
        <EventCard request={{
          personId: 123,
          personName: 'John Doe',
          positionId: 10,
          positionName: 'Guitar',
        }} />
      </TestAuthProvider>
    </CookieUniversalProvider>
  );

  const positionElement = screen.getByText(/Guitar/i);
  const button = screen.getByRole('button');
  expect(positionElement).toBeInTheDocument();
  expect(button).toBeInTheDocument();
});

为什么原代码无效?

原测试中,你在withAuth包裹的组件里获取的auth实例,和EventCard内部通过useAuth获取的实例是两个完全独立的对象。EventCard位于AuthProvider的子组件树中,它会从AuthProvider的Context中获取自己的实例,外层组件的spyOn操作无法影响到它。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 13:00:00