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

React testing-library测试多用例场景下无法重新渲染Modal的问题

测试用例并行执行模态框渲染异常解决方案

问题根因

  • 测试代码异步逻辑写法错误:Promise实例未调用resolve,前序用例的异步状态没有正确结束,状态泄露到后续用例;同时async/await和done回调混用,导致测试生命周期混乱,提前结束用例导致DOM未重置。
  • 模态框使用React Portal实现:React Testing Library默认的cleanup方法只会清理render方法挂载的容器节点,Portal挂载到document.body的模态框节点不会被自动清理,多个用例的模态框实例冲突导致第二个用例无法正常渲染。
  • props引用复用:如果多组测试用例复用同一个props对象引用,前序用例如果修改了props属性,会导致后续用例拿到的初始配置错误。

修复方案

步骤1:配置全局清理逻辑

在测试文件开头的afterEach钩子中,手动清理Portal挂载的节点:

import { cleanup, render, screen, fireEvent } from '@testing-library/react';
import { act } from 'react-dom/test-utils';

afterEach(() => {
  // 清理React Testing Library默认渲染节点
  cleanup();
  // 手动移除挂载在body下的模态框根节点,可根据实际模态框挂载节点的类名/id调整选择器
  const modalRoot = document.querySelector('.modal-root') || document.getElementById('modal-root');
  if (modalRoot) {
    modalRoot.remove();
  }
  // 兜底重置body内容
  document.body.innerHTML = '';
  // 重置所有jest mock调用状态
  jest.clearAllMocks();
});

步骤2:修正测试用例异步写法

去掉冗余的done回调,使用findBy*查询替代getBy*适配模态框的异步渲染逻辑:

// 每个用例重新生成props,避免引用复用导致的属性污染
const getTestProps = () => ({
  visible: true,
  onCancel: jest.fn(),
  onOk: jest.fn(),
  // 其余自定义属性
});

it("点击取消按钮时关闭模态框", async () => {
  render(<MyModal {...getTestProps()} />);
  const modalComponent = await screen.findByTestId('modal-client-id');
  const cancelButton = await screen.findByTestId('modal-cancel-button');

  await act(async () => {
    fireEvent.click(cancelButton);
    // 等待模态框状态更新、关闭动画等异步逻辑执行完成
    await new Promise(resolve => setTimeout(resolve, 0));
  });

  expect(modalComponent).not.toBeInDocument();
});

it("点击确认按钮时关闭模态框", async () => {
  render(<MyModal {...getTestProps()} />);
  const modalComponent = await screen.findByTestId('modal-client-id');
  const okButton = await screen.findByTestId('modal-ok-button');

  await act(async () => {
    fireEvent.click(okButton);
    await new Promise(resolve => setTimeout(resolve, 0));
  });

  expect(modalComponent).not.toBeInDocument();
});

额外排查点

  • 检查模态框组件卸载逻辑是否完整:如果组件绑定了window/body级别的事件监听、定时器,需要在useEffect的清理函数中移除,避免全局事件残留影响后续用例。
  • 排查模态框是否有全局状态依赖:如果模态框的显隐状态由Redux、Mobx等全局状态管理,需要在afterEach中重置全局状态,避免前序用例的状态修改影响后续渲染。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 03:39:02