如何在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
相关产品推荐
相关产品推荐

