material-ui菜单实际运行正常 点击关闭后测试断言菜单消失失败
问题原因
Material UI 内置的 Menu 组件底层基于 Popover 实现,默认带有关闭过渡动画。你点击菜单项触发关闭逻辑后,测试会立即执行断言,此时过渡动画还未执行完成,Menu 的根 DOM 节点尚未被卸载,因此 queryByTestId("lang-menu") 仍然能查询到元素,导致 toBeNull() 断言失败。
浏览器中运行正常是因为普通用户感知不到几十毫秒的过渡延迟,不会有异常感知。
解决方案
推荐三种可行方案,按需选择即可:
方案1:等待元素完全卸载(最符合真实场景)
使用 @testing-library/react 提供的 waitForElementToBeRemoved 工具方法,主动等待 Menu 元素被完全移除后再执行后续校验,测试用例需要改成异步写法:
import { render, screen, waitForElementToBeRemoved } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import React from "react"; import LanguageMenu from "../LanguageMenu"; describe("<LanguageMenu />", () => { // 测试用例加async标识 it("renders menu correctly when opened and closed", async () => { render(<LanguageMenu />); expect(screen.queryByTestId("lang-menu")).toBeNull(); userEvent.click(screen.getByRole("button")); expect(screen.getByTestId("lang-menu")).toMatchSnapshot(); expect(screen.getByTestId("lang-menu")).toBeVisible(); userEvent.click(screen.getByText("Menu Button")); // 等待元素完全卸载,该方法本身会做校验,不需要额外写toBeNull断言 await waitForElementToBeRemoved(screen.getByTestId("lang-menu")); }); });
方案2:禁用Menu组件的过渡动画
如果不需要测试过渡效果,可以直接给Menu组件关闭过渡,关闭后元素会在触发关闭逻辑时立即卸载,同步断言即可生效:
// 组件代码中修改Menu属性 <Menu data-testid="lang-menu" id="test-menu" anchorEl={anchorEl} open={Boolean(anchorEl)} onClick={handleClose} onClose={handleClose} // 新增属性禁用过渡 TransitionComponent={props => <>{props.children}</>} > <MenuItem key="item1" onClick={handleClose}> Menu Button </MenuItem> </Menu>
方案3:使用keepMounted属性时的适配
如果你保留了keepMounted属性(菜单不会卸载,只会隐藏),不需要断言toBeNull,等待过渡完成后断言不可见即可:
// 测试用例修改最后一段断言 userEvent.click(screen.getByText("Menu Button")); // 等待过渡完成,断言不可见 await waitFor(() => { expect(screen.getByTestId("lang-menu")).not.toBeVisible(); })
内容的提问来源于stack exchange,提问作者Cielquan
相关产品推荐
相关产品推荐

