React Testing Library:Portal内容渗透至其他测试DOM的问题求助
解决Vitest + React Testing Library中react-modal的DOM渗透问题
react-modal依赖Portal将内容挂载到全局body上,而Testing Library默认的cleanup只会清理自身渲染的组件,不会处理react-modal给body添加的类、trap元素等全局修改,这就是导致测试间DOM污染的原因。以下是几种实用的解决方法:
1. 单测试用例内手动清理
在涉及react-modal的测试用例中,添加afterEach钩子,手动关闭所有modal并清理残留的DOM和body类:
import ReactModal from 'react-modal'; import { afterEach } from 'vitest'; afterEach(() => { // 关闭所有打开的modal实例 ReactModal.closeAll(); // 移除body上的react-modal专属类 document.body.classList.remove('ReactModal__Body--open'); // 清理页面上残留的modal相关元素(陷阱层、遮罩层等) const modalRelatedElements = document.querySelectorAll( '.ReactModal__Trap, .ReactModal__Overlay, .ReactModal__Content' ); modalRelatedElements.forEach(el => el.remove()); });
2. 全局配置自动清理
在Vitest的全局配置文件(比如vitest.setup.js)中添加全局清理逻辑,避免每个测试用例重复写代码:
import { afterEach } from 'vitest'; import ReactModal from 'react-modal'; afterEach(() => { ReactModal.closeAll(); // 移除body上的react-modal类(兼容多个类共存的情况) document.body.className = document.body.className.replace(/\s*ReactModal__Body--open\s*/g, '').trim(); // 清理所有modal相关DOM元素 document.querySelectorAll('.ReactModal__Trap, .ReactModal__Overlay, .ReactModal__Content').forEach(el => el.remove()); });
3. 指定测试专用的Modal根节点
避免react-modal直接操作全局body,在测试中创建独立的根节点供modal挂载:
import ReactModal from 'react-modal'; import { beforeEach, afterEach } from 'vitest'; beforeEach(() => { // 创建测试专用的modal根节点 const modalRoot = document.createElement('div'); modalRoot.id = 'test-modal-root'; document.body.appendChild(modalRoot); // 告诉react-modal使用这个根节点 ReactModal.setAppElement('#test-modal-root'); }); afterEach(() => { // 移除测试根节点,连带清理所有modal内容 document.getElementById('test-modal-root')?.remove(); // 恢复默认的body作为根节点,避免影响其他测试 ReactModal.setAppElement('body'); });
4. 确保测试等待异步操作完成
react-modal的打开/关闭可能是异步的,测试中需使用waitFor或findBy*系列方法等待模态框渲染完成,避免未完成的操作留下残留:
test('测试模态框打开逻辑', async () => { render(<YourComponentWithModal />); // 触发打开模态框的操作 fireEvent.click(screen.getByText('打开弹窗')); // 等待模态框渲染完成 await screen.findByRole('dialog'); // 执行断言逻辑 expect(screen.getByText('弹窗内容')).toBeInTheDocument(); });
内容的提问来源于stack exchange,提问作者Niall Curtis
相关产品推荐
相关产品推荐

