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

React Native中如何测试基于useRef的Gorhom底部弹窗打开功能?

问题

我在React Native开发中,需要测试点击按钮时,基于Gorhom Bottom Sheet库开发的底部弹窗能否正常打开。这个弹窗通过useRef调用present方法实现打开操作,但遇到了两难问题:

  • 不Mock useRef上的方法时,测试报错:_addChannelBsRef$curr.present is not a function
  • 若Mock useRef的方法,弹窗又无法实际打开,没法验证弹窗是否真的显示。

相关代码示例

触发打开的组件

const VerticalKlubListItemNewChannel = ({
  openAddChannelBs,
  selectedKlub,
}: VerticalKlubListItemNewChannelProps) => {
  const pressAddNewChannel = () => {
    openAddChannelBs();
  };

  return (
    <TouchableOpacity
      testID={BtnTestIds.openNewChannelBtn}
      onPress={pressAddNewChannel}
      style={styles.container}>
      <CustomText color={colorsTheme.primary} fontWeight="semiBold">
        new channel
      </CustomText>
    </TouchableOpacity>
  ) : null;
};

传递给组件的打开函数

const openAddChannelBs = useCallback(() => {
  addChannelBsRef.current?.present();
}, []);

我的测试代码

describe("AddNewChannelBottomSheet", () => {
  it("open the bottom sheet onPress button", async () => {
    const { getByTestId } = render(
      <LeftDrawerChannel navigation={navigation as any} />,
      {
        preloadedState: {
          channel: {
            currentSelectedKlub: { user: { klubRole: "admin" } },
          } as any,
        },
      },
    );

    const { getByText } = render(
      <AddNewchannelBs
        addChannelBsRef={
          mockOpenBsRef as unknown as BottomSheetModaleContainerRefType
        }
      />,
    );

    const openBsAddNewchannel = getByTestId(BtnTestIds.openNewChannelBtn);

    expect(openBsAddNewchannel).toBeTruthy();

    fireEvent.press(openBsAddNewchannel);

    const getText = getByText(
      "Enter the name of the channel that will be visible to members",
    );

    await waitFor(() => {
      expect(getText).toBeTruthy();
    });
  });
});

解决方案

1. 正确模拟Bottom Sheet的Ref,兼顾方法跟踪与组件渲染

不要直接Mock useRef,而是创建一个模拟Ref对象,给它的current属性添加present方法,用jest.fn()跟踪调用状态,同时保证弹窗组件能正常渲染:

const mockAddChannelBsRef = {
  current: {
    present: jest.fn(),
  },
};

2. 把触发组件和弹窗放在同一个渲染上下文里

你之前两次调用render,导致触发组件和弹窗不在同一个React树中,点击按钮的事件无法关联到弹窗。必须将它们放在同一个render调用中,或者确保openAddChannelBs指向模拟Ref的present方法。调整后的测试代码如下:

describe("AddNewChannelBottomSheet", () => {
  it("opens the bottom sheet when button is pressed", async () => {
    const mockAddChannelBsRef = {
      current: {
        present: jest.fn(),
      },
    };

    const mockOpenAddChannelBs = () => {
      mockAddChannelBsRef.current?.present();
    };

    const { getByTestId, getByText } = render(
      <>
        <VerticalKlubListItemNewChannel 
          openAddChannelBs={mockOpenAddChannelBs}
          selectedKlub={{ user: { klubRole: "admin" } }}
        />
        <AddNewchannelBs addChannelBsRef={mockAddChannelBsRef as any} />
      </>,
      {
        preloadedState: {
          channel: {
            currentSelectedKlub: { user: { klubRole: "admin" } },
          } as any,
        },
      },
    );

    const openBtn = getByTestId(BtnTestIds.openNewChannelBtn);
    expect(openBtn).toBeTruthy();

    fireEvent.press(openBtn);

    // 先验证present方法被触发
    expect(mockAddChannelBsRef.current.present).toHaveBeenCalled();

    // 再验证弹窗内容是否显示
    await waitFor(() => {
      expect(getByText("Enter the name of the channel that will be visible to members")).toBeTruthy();
    });
  });
});

3. 针对Gorhom库的特殊Mock(可选)

如果上述方法仍有问题,直接Mock Gorhom的BottomSheetModal组件,让它在测试环境下渲染内部内容,同时不影响方法跟踪:

// 在测试文件顶部添加Mock
jest.mock('@gorhom/bottom-sheet', () => ({
  ...jest.requireActual('@gorhom/bottom-sheet'),
  BottomSheetModal: ({ children }) => <>{children}</>,
}));

这样弹窗内容会被正常渲染,你可以直接验证内容是否存在,同时结合Ref Mock确认present方法的调用。

4. 杜绝多次渲染

测试中只能调用一次render,多次渲染会生成独立的React树,状态和事件完全不互通,这是你之前测试失败的核心原因之一。

内容的提问来源于stack exchange,提问作者yoann84

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 18:42:50