如何用Jest和React Testing Library Mock @react-navigation/native并修复测试问题
问题描述
开发的React Native Header组件,触发返回按钮的onPress事件时仅打印onPressCalled,未进入if (navigation.canGoBack())分支,测试中的navigation mock未生效,导致断言goBack被调用失败。
组件代码
import { ArrowIcon, HeaderComponent, IconWrapper, PageTitle, PlaceholderView, } from './Header.style'; import { Poppins_500Medium, useFonts } from '@expo-google-fonts/poppins'; import { useNavigation } from '@react-navigation/native'; const Header = () => { const navigation = useNavigation(); const [loaded, error] = useFonts({ Poppins_500Medium, }); if (error || !loaded) { return <></>; } return ( <HeaderComponent> <IconWrapper testID="IconWrapper" onPress={() => { console.log('onPressCalled'); if (navigation.canGoBack()) { console.log('usao1'); navigation.goBack(); } }} > <ArrowIcon name='left' color='#fff' size={24} /> </IconWrapper> <PageTitle>Create account</PageTitle> <PlaceholderView /> </HeaderComponent> ); }; export default Header;
测试代码(修正版)
import { fireEvent, render, waitFor } from '../../../test-utils'; describe('Header', () => { it.only('should navigate to previous screen when back button is clicked', async () => { // 重置模块缓存,确保每次测试加载最新的mock jest.resetModules(); const navigation = { canGoBack: jest.fn().mockReturnValue(true), goBack: jest.fn(), }; // 先mock依赖,再动态导入组件 jest.doMock('@react-navigation/native', () => ({ useNavigation: jest.fn().mockReturnValue(navigation), })); jest.doMock('@expo-google-fonts/poppins', () => ({ Poppins_500Medium: 'Poppins_500Medium', useFonts: jest.fn().mockReturnValue([true, null]), })); // 动态导入Header组件,确保使用mock后的依赖 const Header = (await import('./Header.component')).default; const { findByTestId } = render(<Header />); const iconWrapper = await findByTestId('IconWrapper'); fireEvent.press(iconWrapper); await waitFor(() => { expect(navigation.canGoBack).toHaveBeenCalled(); expect(navigation.goBack).toHaveBeenCalled(); }); }); })
核心解决思路
调整Mock执行顺序
原测试中jest.doMock在组件导入后执行,此时组件已加载真实的useNavigation依赖,mock无法生效。通过jest.resetModules()重置模块缓存,配合动态导入组件,确保组件加载时使用的是mock后的依赖。确保元素可被测试定位
给IconWrapper添加testID="IconWrapper"属性,保证findByTestId能正确找到交互元素。可选方案:使用jest.mock自动提升
也可以用jest.mock替代jest.doMock,该方法会被自动提升到文件顶部,确保组件导入前就完成mock:import { fireEvent, render, waitFor } from '../../../test-utils'; import { useNavigation } from '@react-navigation/native'; jest.mock('@react-navigation/native', () => ({ useNavigation: jest.fn(), })); jest.mock('@expo-google-fonts/poppins', () => ({ Poppins_500Medium: 'Poppins_500Medium', useFonts: jest.fn().mockReturnValue([true, null]), })); import Header from './Header.component'; describe('Header', () => { it.only('should navigate to previous screen when back button is clicked', async () => { const navigation = { canGoBack: jest.fn().mockReturnValue(true), goBack: jest.fn(), }; useNavigation.mockReturnValue(navigation); const { findByTestId } = render(<Header />); const iconWrapper = await findByTestId('IconWrapper'); fireEvent.press(iconWrapper); await waitFor(() => { expect(navigation.canGoBack).toHaveBeenCalled(); expect(navigation.goBack).toHaveBeenCalled(); }); }); })
内容的提问来源于stack exchange,提问作者Andrija Gajic
相关产品推荐
相关产品推荐

