在Jest中模拟Auth Context与Provider的最优方案
问题描述
我在项目中需要频繁测试带有认证功能的组件,以下是Auth Context的设置示例:
AuthContext 代码
Navigation/contexts/AuthContext.js
const AuthContext = createContext(); const AuthProvider = ({children}) => { const [auth, setAuth] = useState(false); return ( <AuthContext.Provider value={{auth, setAuth}}> {children} </AuthContext.Provider> ); }; export const useAuth = () => useContext(AuthContext); export default AuthProvider;
待测试组件 LoginLoadingScreen
Navigation/Screens/Components/LoginLoadingScreen.js
import {useAuth} from '../../contexts/AuthContext'; const LoginLoadingScreen = ({navigation}) => { const {setAuth} = useAuth(); // 组件逻辑... };
测试代码及报错
Navigation/Screens/Components/tests/LoginLoadingScreen.test.js
import React from 'react'; import LoginLoadingScreen from '../../Components/LoginLoadingScreen'; import {render, screen, fireEvent} from '@testing-library/react-native'; import {useAuth} from '../../../contexts/AuthContext'; test('LoginLoadingScreen', () => { render(<LoginLoadingScreen />); });
运行测试后出现错误:
TypeError: Cannot read properties of undefined (reading 'setAuth') 15 | 16 | const LoginLoadingScreen = ({navigation}) => { > 17 | const {setAuth} = useAuth();
由于后续会有大量需要模拟认证的测试场景,请问最优的模拟方案是什么?
最优模拟方案
针对频繁测试带认证功能的组件,推荐两种可复用的方案:
方案一:自定义测试用AuthProvider包装器
创建一个可配置的测试用Provider,方便在不同测试场景中快速注入不同的auth状态或mock函数,适合大规模复用:
// tests/utils/test-auth-provider.js import React, {createContext, useContext, useState} from 'react'; export const TestAuthContext = createContext(); export const TestAuthProvider = ({children, initialAuth = false, mockSetAuth = jest.fn()}) => { const [auth, setAuth] = useState(initialAuth); return ( <TestAuthContext.Provider value={{ auth, setAuth: mockSetAuth || setAuth }}> {children} </TestAuthContext.Provider> ); }; // 全局mock原useAuth钩子,让所有测试都使用测试用上下文 jest.mock('../../contexts/AuthContext', () => ({ useAuth: () => useContext(TestAuthContext) }));
在测试中使用:
// LoginLoadingScreen.test.js import {TestAuthProvider} from '../../../utils/test-auth-provider'; test('LoginLoadingScreen 触发认证状态更新', () => { const mockSetAuth = jest.fn(); render( <TestAuthProvider mockSetAuth={mockSetAuth}> <LoginLoadingScreen /> </TestAuthProvider> ); // 触发组件中调用setAuth的逻辑后验证 expect(mockSetAuth).toHaveBeenCalled(); });
方案二:直接mock useAuth钩子
如果不需要完整的上下文环境,直接mockuseAuth返回预设值,适合单组件的简单测试场景:
// LoginLoadingScreen.test.js import {useAuth} from '../../../contexts/AuthContext'; // 单测内mock钩子,返回预设的auth状态和mock函数 jest.mock('../../../contexts/AuthContext', () => ({ useAuth: jest.fn(() => ({ auth: false, setAuth: jest.fn() })) })); test('LoginLoadingScreen 正常渲染', () => { render(<LoginLoadingScreen />); // 断言渲染结果... }); test('LoginLoadingScreen 处理已认证状态', () => { const mockSetAuth = jest.fn(); // 动态修改mock返回值 useAuth.mockReturnValue({auth: true, setAuth}); render(<LoginLoadingScreen />); // 触发相关操作后验证 expect(mockSetAuth).toHaveBeenCalledWith(true); });
方案选择建议
- 若需要在大量测试中复用不同的认证状态或mock行为,优先用方案一,它更贴合组件实际的上下文使用方式,配置灵活度更高。
- 针对单个简单测试场景,方案二更轻便,无需额外包装组件。
内容的提问来源于stack exchange,提问作者Fernando Maymone
相关产品推荐
相关产品推荐

