使用react-native-testing-library测试Reanimated Toast组件时ref为空
解决React Native Testing Library测试Reanimated Toast时Ref为null的问题
核心问题分析
renderHook和render属于独立渲染上下文,toastRef在renderHook中创建后,传递给<Toast>组件时可能还未完成ref绑定就调用了show方法- 启用
jest.useFakeTimers()后,无法自动捕获react-native-reanimated的动画定时器,导致动画时间模拟失效 waitFor的超时设置为500ms,小于Toast实际需要的700ms显示时间- 测试ID存在拼写错误:
taost-message应为toast-message
具体修复步骤
1. 统一渲染上下文,确保Ref绑定完成
将Hook与Toast组件放在同一个渲染上下文内,避免跨上下文的ref传递问题,同时等待ref完成挂载:
// 替换原有的renderHook+render写法 it('renders toast message correctly', async () => { let showToast; let checkIsVisible; let toastRef; const { getByTestId } = render( <SafeAreaProvider> {() => { const hookResult = useToast(); toastRef = hookResult.toastRef; showToast = hookResult.show; checkIsVisible = hookResult.isVisible; return <Toast ref={toastRef} />; }} </SafeAreaProvider> ); // 等待ref完成绑定 await waitFor(() => expect(toastRef.current).not.toBeNull()); });
2. 正确处理Reanimated动画
react-native-reanimated的动画不依赖Jest假定时器,可直接禁用动画来跳过等待时间:
import { disableAnimations } from 'react-native-reanimated'; beforeAll(() => { disableAnimations(); });
如果需要保留动画测试,可安装jest-reanimated库来适配定时器:
npm install --save-dev jest-reanimated
并在Jest配置的setupFilesAfterEnv中添加:
"setupFilesAfterEnv": ["jest-reanimated"]
3. 调整超时时间并修复测试ID
将waitFor的超时设置为大于700ms,同时修正测试ID的拼写错误:
act(() => { showToast({ type: 'success', message: 'Hello, world!', }); }); // 超时时间调整为1000ms,确保Toast有足够时间显示 await waitFor( () => { expect(checkIsVisible()).toBeTruthy(); }, { timeout: 1000 } ); // 修正测试ID拼写 const message = getByTestId('toast-message'); expect(message).toHaveTextContent('Hello, world!');
完整修复后的测试代码
import React from 'react'; import { render, act, waitFor } from '@testing-library/react-native'; import { Toast, useToast } from '..'; import { SafeAreaProvider } from 'react-native-safe-area-context'; import { disableAnimations } from 'react-native-reanimated'; beforeAll(() => { disableAnimations(); }); describe('Toast', () => { it('renders toast message correctly', async () => { let showToast; let checkIsVisible; let toastRef; const { getByTestId } = render( <SafeAreaProvider> {() => { const hookResult = useToast(); toastRef = hookResult.toastRef; showToast = hookResult.show; checkIsVisible = hookResult.isVisible; return <Toast ref={toastRef} />; }} </SafeAreaProvider> ); await waitFor(() => expect(toastRef.current).not.toBeNull()); act(() => { showToast({ type: 'success', message: 'Hello, world!', }); }); await waitFor( () => { expect(checkIsVisible()).toBeTruthy(); }, { timeout: 1000 } ); const message = getByTestId('toast-message'); expect(message).toHaveTextContent('Hello, world!'); }); });
内容的提问来源于stack exchange,提问作者Pakenfit
相关产品推荐
相关产品推荐

