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

在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 02:33:23