如何用React Testing Library测试React-Bootstrap NavDropdown组件
我尝试用React Testing Library测试带有data-testid属性的React-Bootstrap NavDropdown元素,但测试库找不到子元素的data-testid属性。
MyNav.tsx
import Nav from 'react-bootstrap/Nav'; import Navbar from 'react-bootstrap/Navbar'; import NavDropdown from 'react-bootstrap/NavDropdown'; import LOGO from './logo.svg'; const MyNav = (): JSX.Element => ( <Navbar collapseOnSelect expand="lg" bg="dark" variant="dark"> <Navbar.Brand href="/"> <img src={LOGO} width="30" height="30" alt="React Bootstrap logo" /> BBS </Navbar.Brand> <Navbar.Toggle aria-controls="responsive-navbar-nav" /> <Navbar.Collapse id="responsive-navbar-nav"> <Nav className="mr-auto"> <NavDropdown title="English" id="navbarScrollingDropdown" data-testid="en-boards" > <NavDropdown.Item href="#en-news" data-testid="en-news"> News </NavDropdown.Item> <NavDropdown.Item href="#en-sensitive-may"> Politics </NavDropdown.Item> </NavDropdown> <NavDropdown title="日本語" id="navbarScrollingDropdown"> <NavDropdown.Item href="#ja-news">ニュース</NavDropdown.Item> <NavDropdown.Item href="#ja-sensitive-may">政治</NavDropdown.Item> </NavDropdown> </Nav> </Navbar.Collapse> </Navbar> ); export default MyNav;
MyNav.test.tsx
import React from 'react'; import { render, screen, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import MyNav from './MyNav'; test('renders nav', async () => { const user = userEvent.setup(); render(<MyNav />); const navBarBrandEl = screen.getByText('BBS'); const navDropDownElEn = screen.getByText('English'); const navDropDownElJa = screen.getByText('日本語'); expect(navBarBrandEl).toBeInTheDocument(); expect(navDropDownElEn).toBeInTheDocument(); expect(navDropDownElJa).toBeInTheDocument(); const enBoards = screen.getByTestId('en-boards'); await user.click(enBoards); const enNews = within(enBoards).getByTestId('en-news'); expect(enNews).toBeInTheDocument(); });
测试结果
TestingLibraryElementError: Unable to find an element by: [data-testid="en-news"] Ignored nodes: comments, script, style <div class="nav-item dropdown" data-testid="en-boards" > <a aria-expanded="false" class="dropdown-toggle nav-link" href="#" id="navbarScrollingDropdown" role="button" tabindex="0" > English </a> </div> 18 | const enBoards = screen.getByTestId('en-boards'); 19 | await user.click(enBoards); > 20 | const enNews = within(enBoards).getByTestId('en-news'); | ^ 21 | 22 | expect(enNews).toBeInTheDocument(); 23 | }); at Object.getElementError (node_modules/@testing-library/dom/dist/config.js:40:19) at node_modules/@testing-library/dom/dist/query-helpers.js:90:38 at node_modules/@testing-library/dom/dist/query-helpers.js:62:17 at getByTestId (node_modules/@testing-library/dom/dist/query-helpers.js:111:19) at Object.<anonymous> (src/MyNav.test.tsx:20:35)
环境信息
- macOS 12.5
- TypeScript 4.8.4
- React 18.2.0
- React-Bootstrap 2.5.0
- testing-library/react 13.4.0
我试过相关方案但没有效果,恳请帮忙解决。
解决方案
核心原因
React-Bootstrap的NavDropdown组件会把下拉菜单渲染到body元素下,而非NavDropdown自身的DOM结构内,导致within(enBoards)无法找到子元素——因为子元素不在这个容器的DOM层级里。
修复方案1:全局异步查找子元素
直接使用screen.findByTestId(异步方法,会自动等待元素渲染)查找下拉子元素,无需局限于父容器:
import React from 'react'; import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import MyNav from './MyNav'; test('renders nav and dropdown items', async () => { const user = userEvent.setup(); render(<MyNav />); // 验证基础导航元素 expect(screen.getByText('BBS')).toBeInTheDocument(); expect(screen.getByText('English')).toBeInTheDocument(); expect(screen.getByText('日本語')).toBeInTheDocument(); // 触发下拉菜单展开 const enBoards = screen.getByTestId('en-boards'); await user.click(enBoards); // 异步等待子元素渲染并查找 const enNews = await screen.findByTestId('en-news'); expect(enNews).toBeInTheDocument(); });
修复方案2:给下拉菜单容器添加testid(可选)
通过NavDropdown的menuProps属性,给下拉菜单容器传递data-testid,之后可以用within精准查找:
- 修改MyNav.tsx中的NavDropdown:
<NavDropdown title="English" id="navbarScrollingDropdown" data-testid="en-boards" menuProps={{ 'data-testid': 'en-boards-menu' }} > {/* 子元素不变 */} </NavDropdown>
- 修改测试代码:
await user.click(enBoards); // 先等待菜单容器出现 const enBoardsMenu = await screen.findByTestId('en-boards-menu'); // 在菜单容器内查找子元素 const enNews = within(enBoardsMenu).getByTestId('en-news'); expect(enNews).toBeInTheDocument();
内容的提问来源于stack exchange,提问作者ykmr
相关产品推荐
相关产品推荐

