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

如何解决Expo React Native中Jest测试useAuth0().authorize调用未触发问题?

登录按钮点击未触发Auth0 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 |     })

问题原因及解决方案

核心问题

  1. mock对象不固定导致断言失效:当前useAuth0的mock每次调用都会返回一个全新的对象,组件渲染时拿到的authorize是一个mock函数,而测试断言时调用useAuth0().authorize得到的是另一个全新的mock函数,自然检测不到调用记录。
  2. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 18:30:17