使用React Testing Library的findByRole检测react-modal存在报错
React Testing Library 无法检测到 react-modal 的 dialog 角色
问题描述
尝试使用 React Testing Library 的 findByRole 方法检测 react-modal 的存在时,持续收到错误提示:Unable to find role="dialog",但测试控制台中清晰可见带有该角色的元素。
测试代码
import React from "react"; import { render, screen, within, fireEvent, cleanup, waitFor } from '@testing-library/react'; import '@testing-library/jest-dom'; import "@testing-library/react/dont-cleanup-after-each"; import Modal from 'react-modal'; import PackagerModal from './PackagerModal'; const mockedEmptyFn = jest.fn(); const mockBaseProps = { openPackager: true, setOpenPackager: mockedEmptyFn, activeOrder: { // 无关属性省略 }, setOrders: mockedEmptyFn, customStyles: { content: { backgroundColor: "var(--color-primary)", border: "1px solid #ccc", boxShadow: "-2rem 2rem 2rem rgba(0, 0, 0, 0.5)", color: "rgba(var(--RGB-text), 0.8)", filter: "blur(0)", fontSize: "1.1em", fontWeight: "bold", margin: "50px auto", opacity: 1, outline: 0, position: "relative", visibility: "visible", width: "500px" }, overlay: { backgroundColor: "rgba(255, 255, 255, 0.9)" } }, readOnly: false, setReadOnly: mockedEmptyFn, }; Modal.setAppElement('body'); const Component = (props) => <PackagerModal {...mockBaseProps} {...props} /> describe('Packager Modal tests with editable inputs', () => { afterAll(() => { cleanup(); }); test('Should show packager modal', async () => { render( <Component/> ); // screen.debug(); const modalWindow = await screen.findByRole('dialog'); expect(modalWindow).toBeInTheDocument(); }); });
模态框组件代码
import ReactModal from 'react-modal'; import React, { useEffect, useRef, useState } from 'react'; import CloseButton from './CloseButton'; import PropTypes from 'prop-types'; const PackagerModal = (props) => { const { openPackager, setOpenPackager, activeOrder, setOrders, customStyles, readOnly, setReadOnly, } = props; const cleanUpModal = () => { setReadOnly(false); setUserInput(initialState); }; return ( <ReactModal isOpen={openPackager} style={customStyles} className={'order-details-modal'} closeTimeoutMS={1000} onAfterClose={cleanUpModal} > <CloseButton setOpenModal={setOpenPackager} /> <h2 className={'title'}>Packager Order Checklist</h2> </ReactModal> ); }; PackagerModal.propTypes = { openPackager: PropTypes.bool.isRequired, setOpenPackager: PropTypes.func.isRequired, customStyles: PropTypes.object.isRequired, activeOrder: PropTypes.object.isRequired, setOrders: PropTypes.func.isRequired, readOnly: PropTypes.bool.isRequired, setReadOnly: PropTypes.func.isRequired, }; export default PackagerModal;
测试控制台输出
● Packager Modal tests with editable inputs › Should show packager modal Unable to find role="dialog" Ignored nodes: comments, script, style <body aria-hidden="true" class="ReactModal__Body--open" > <div data-react-modal-body-trap="" style="position: absolute; opacity: 0;" tabindex="0" /> <div /> <div data-react-modal-body-trap="" style="position: absolute; opacity: 0;" tabindex="0" /> <div class="ReactModalPortal" > <div class="ReactModal__Overlay ReactModal__Overlay--after-open" style="position: fixed; top: 0px; left: 0px; right: 0px; bottom: 0px; background-color: rgba(255, 255, 255, 0.9);" > <div aria-modal="true" class="ReactModal__Content ReactModal__Content--after-open order-details-modal" role="dialog" style="border: 1px solid #ccc; box-shadow: -2rem 2rem 2rem rgba(0, 0, 0, 0.5); filter: blur(0); font-size: 1.1em; font-weight: bold; margin: 50px auto; opacity: 1; outline: 0; position: relative; visibility: visible; width: 500px;" tabindex="-1" > ...
问题原因
从控制台输出可见,modal 打开时,body 被添加了 aria-hidden="true" 属性,而 react-modal 的内容挂载在 body 下的 ReactModalPortal 中。React Testing Library 默认会忽略带有 aria-hidden="true" 的元素及其子元素,因此即使 modal 本身带有 role="dialog" 和 aria-modal="true",也会被过滤,导致查询失败。
解决方案
方案1:修改 react-modal 的 appElement 配置
不要将 body 设为 appElement,改用页面的根容器(如 #root)。这样 react-modal 只会将根容器标记为 aria-hidden,而 modal 作为 body 的直接子元素,不会被包含在 aria-hidden 的容器内:
// 测试代码中修改 import Modal from 'react-modal'; Modal.setAppElement('#root'); // 替换为你的根容器选择器
或在组件中配置:
// PackagerModal.js import ReactModal from 'react-modal'; ReactModal.setAppElement('#root');
方案2:查询时允许查找隐藏元素
在 findByRole 中添加 { hidden: true } 选项,让 RTL 不忽略 aria-hidden 容器内的元素:
test('Should show packager modal', async () => { render(<Component />); const modalWindow = await screen.findByRole('dialog', { hidden: true }); expect(modalWindow).toBeInTheDocument(); });
方案3:通过模态框内的可见内容查询
直接查询模态框内的可见文本(如标题),间接验证模态框存在,这更符合 RTL「测试用户实际可见行为」的设计理念:
test('Should show packager modal', async () => { render(<Component />); expect(await screen.findByText('Packager Order Checklist')).toBeInTheDocument(); });
内容的提问来源于stack exchange,提问作者dmikester1
相关产品推荐
相关产品推荐

