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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 20:52:48