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

如何在TypeScript+Jest中优雅Mock react-oidc-context的useAuth返回值?

优雅解决react-oidc-context的Jest Mock问题

针对react-oidc-context的useAuth钩子Mock痛点,以下是几种实用且符合TypeScript类型要求的解决方案:

方案1:全局基础Mock + 局部属性覆盖

先在测试文件顶部全局MockuseAuth,提供所有必要属性的默认值(空函数、基础布尔值等),再在单个测试用例中仅覆盖需要修改的属性,既满足类型校验,又避免重复编写全量属性。

// 测试文件顶部全局Mock
jest.mock('react-oidc-context', () => ({
  useAuth: jest.fn(() => ({
    isLoading: false,
    isAuthenticated: false,
    user: null,
    signIn: jest.fn(),
    signOut: jest.fn(),
    signinRedirect: jest.fn(),
    signoutRedirect: jest.fn(),
    // 补充库定义的其他必填属性,可从AuthContextType类型中提取默认空值
  })),
}));

// 单个测试用例中覆盖目标属性
test('已认证状态下渲染用户信息', () => {
  const { useAuth } = require('react-oidc-context');
  // 继承全局Mock的默认属性,仅修改需要的部分
  useAuth.mockReturnValue({
    ...useAuth.mock.results[0].value,
    isAuthenticated: true,
    user: { name: 'Test User', email: 'test@example.com' },
  });

  render(<UserProfile />);
  expect(screen.getByText('Test User')).toBeInTheDocument();
});

方案2:封装Mock工具函数生成符合类型的对象

编写一个工具函数,基于AuthContextType生成带默认值的Mock对象,测试用例仅需传入差异属性,自动补全剩余默认值,彻底解决手动编写全量属性的麻烦。

// src/test-utils/mock-auth.ts
import type { AuthContextType } from 'react-oidc-context';

export const createMockAuth = (overrides?: Partial<AuthContextType>): AuthContextType => ({
  isLoading: false,
  isAuthenticated: false,
  user: null,
  signIn: jest.fn(),
  signOut: jest.fn(),
  signinRedirect: jest.fn(),
  signoutRedirect: jest.fn(),
  error: null,
  // 补充所有库定义的必填属性
  ...overrides,
});

// 测试文件中使用
jest.mock('react-oidc-context', () => ({
  useAuth: jest.fn(() => createMockAuth()),
}));

test('未认证状态下显示登录按钮', () => {
  const { useAuth } = require('react-oidc-context');
  useAuth.mockReturnValue(createMockAuth({
    isAuthenticated: false,
    isLoading: false,
  }));

  render(<AuthGate />);
  expect(screen.getByRole('button', { name: /登录/i })).toBeInTheDocument();
});

方案3:自定义测试用AuthProvider

如果需要在测试中模拟完整的上下文环境,可以自己实现一个测试用的MockAuthProvider,通过props传入需要覆盖的状态,让组件内部的useAuth直接读取上下文值,彻底规避Mock钩子的问题。

// src/test-utils/MockAuthProvider.tsx
import { createContext, useContext } from 'react';
import type { AuthContextType } from 'react-oidc-context';

// 创建Mock上下文
const MockAuthContext = createContext<AuthContextType>({
  isLoading: false,
  isAuthenticated: false,
  user: null,
  signIn: jest.fn(),
  signOut: jest.fn(),
  // 补充所有必填属性的默认值
});

// 覆盖react-oidc-context的useAuth,让它读取我们的Mock上下文
jest.mock('react-oidc-context', () => ({
  useAuth: () => useContext(MockAuthContext),
}));

export const MockAuthProvider = ({
  children,
  auth,
}: {
  children: React.ReactNode;
  auth?: Partial<AuthContextType>;
}) => {
  const baseAuth: AuthContextType = {
    isLoading: false,
    isAuthenticated: false,
    user: null,
    signIn: jest.fn(),
    signOut: jest.fn(),
    ...auth,
  };

  return (
    <MockAuthContext.Provider value={baseAuth}>
      {children}
    </MockAuthContext.Provider>
  );
};

// 测试用例中使用
test('加载状态下显示加载动画', () => {
  render(
    <MockAuthProvider auth={{ isLoading: true }}>
      <AuthGate />
    </MockAuthProvider>
  );

  expect(screen.getByRole('progressbar')).toBeInTheDocument();
});

解决TypeError: Cannot redefine property: useAuth问题

出现该错误是因为重复Mock了useAuth属性,解决方式:

  • 确保jest.mock仅在测试文件顶部执行一次,不要在beforeEach或测试用例中重复调用
  • 如果需要动态重置Mock,使用jest.spyOn修改mockImplementation而非重新Mock模块:
    const useAuthSpy = jest.spyOn(require('react-oidc-context'), 'useAuth');
    useAuthSpy.mockImplementation(() => createMockAuth({ isAuthenticated: true }));
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 03:43:31