如何解决Expo React Native中Jest测试useAuth0().authorize调用未触发问题?
我尝试用@testing-library/react-native编写单元测试,验证点击登录按钮时useAuth0().authorize方法是否被调用,但预期调用1次实际检测到0次。已经尝试过修改点击事件触发方式、替换按钮组件,都没解决问题。
登录页组件(./app/(login)/index.tsx)
import { StyleSheet, TouchableOpacity, SafeAreaView, Text } from 'react-native'; // 注:原代码漏导入Text组件,此处补充 import { useAuth0 } from 'react-native-auth0'; export default function Landing(): React.JSX.Element { const { authorize, clearSession, user } = useAuth0(); const onLogin = async () => { try { await authorize(); } catch (e) { console.log(e); } }; const loggedIn = user !== undefined && user !== null; const onLogout = async () => { try { await clearSession(); } catch (e) { console.log('Log out cancelled'); } }; return ( <SafeAreaView style={styles.container}> <TouchableOpacity style={styles.logBtn} onPress={loggedIn ? onLogout : onLogin} testID='loginBtn'> {user && <Text style={styles.btnTxt}>Log Out</Text>} {!user && <Text style={styles.btnTxt}>Log In</Text>} </TouchableOpacity> </SafeAreaView> ); } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', }, logBtn: { padding: 10, backgroundColor: '#0066cc', borderRadius: 5, }, btnTxt: { color: '#fff', fontSize: 16, }, });
测试代码
import { render, fireEvent, waitFor } from '@testing-library/react-native'; import { useAuth0 } from 'react-native-auth0'; import Landing from '@/app/(login)/index'; jest.mock('react-native-auth0', () => ({ useAuth0: jest.fn(() => ({ authorize: jest.fn(), })), })); describe('Login page renders and performs correct actions through Auth0', () => { test('Login button correctly calls authorize function', async () => { const { getByTestId } = render(<Landing />); const loginBtn = getByTestId('loginBtn'); fireEvent.press(loginBtn) await waitFor(() => { expect(useAuth0().authorize).toHaveBeenCalledTimes(1); }) }) })
错误信息
expect(jest.fn()).toHaveBeenCalledTimes(expected) Expected number of calls: 1 Received number of calls: 0 43 | fireEvent.press(loginBtn) 44 | > 45 | await waitFor(() => { | ^ 46 | expect(useAuth0().authorize).toHaveBeenCalledTimes(1); 47 | }) 48 | })
问题原因及解决方案
核心问题
- mock对象不固定导致断言失效:当前
useAuth0的mock每次调用都会返回一个全新的对象,组件渲染时拿到的authorize是一个mock函数,而测试断言时调用useAuth0().authorize得到的是另一个全新的mock函数,自然检测不到调用记录。 - mock未明确
user值(隐性问题):虽然默认user为undefined会触发登录分支,但显式定义user值能避免逻辑歧义;另外原组件漏导入Text会导致渲染失败,间接影响测试执行。
修正后的测试代码
import { render, fireEvent } from '@testing-library/react-native'; import { useAuth0 } from 'react-native-auth0'; import Landing from '@/app/(login)/index'; // 提前创建固定的mock函数,确保组件与测试使用同一实例 const mockAuthorize = jest.fn(); const mockClearSession = jest.fn(); jest.mock('react-native-auth0', () => ({ useAuth0: jest.fn(() => ({ authorize: mockAuthorize, clearSession: mockClearSession, user: null, // 显式设置user为null,确保走登录分支 })), })); describe('Login page renders and performs correct actions through Auth0', () => { // 每次测试前重置mock调用记录,避免跨测试干扰 beforeEach(() => { mockAuthorize.mockClear(); mockClearSession.mockClear(); }); test('Login button correctly calls authorize function', async () => { const { getByTestId } = render(<Landing />); const loginBtn = getByTestId('loginBtn'); fireEvent.press(loginBtn); // 直接断言固定的mock函数,无需waitFor(组件未依赖authorize返回值更新UI) expect(mockAuthorize).toHaveBeenCalledTimes(1); }); // 补充登出分支测试 test('Logout button correctly calls clearSession function', async () => { // 临时修改mock返回的user值,触发登出分支 (useAuth0 as jest.Mock).mockReturnValue({ authorize: mockAuthorize, clearSession: mockClearSession, user: { name: 'Test User' }, }); const { getByTestId } = render(<Landing />); const logoutBtn = getByTestId('loginBtn'); fireEvent.press(logoutBtn); expect(mockClearSession).toHaveBeenCalledTimes(1); }); })
关键修改点
- 固定mock函数实例:提前创建
mockAuthorize和mockClearSession,确保组件使用的函数与测试断言的是同一个,避免因mock对象频繁重建导致断言失效。 - 显式控制
user值:通过mock返回明确的user状态,精准触发登录/登出分支,消除逻辑歧义。 - 移除不必要的
waitFor:组件点击后仅调用authorize,无UI状态更新,无需等待即可直接断言。 - 添加mock重置逻辑:在每个测试前清空调用记录,避免不同测试间的状态污染。
内容的提问来源于stack exchange,提问作者Leone Varando
相关产品推荐
相关产品推荐

