React、Jest 和 Material-UI:如何测试以模态或弹出框呈现的内容
Posted
技术标签:
【中文标题】React、Jest 和 Material-UI:如何测试以模态或弹出框呈现的内容【英文标题】:React, Jest and Material-UI: How to test for content rendered in a modal or popover 【发布时间】:2018-01-15 01:23:49 【问题描述】:有一些 material-ui 组件不会将其结果呈现在与其父级放置的组件相同的位置。其中我们有Dialog
、Menu
等。
这使得在 jest.js 包装器中测试其内容的存在显然是不可能的,其中安装了一些父组件。
例如给定以下组件:
class DropdownMenu extends React.Component
onButtonClick = (e) =>
this.setState( open: true, anchorEl: e.currentTarget );
render()
return (
<div>
<Button onClick=this.onButtonClick>Menu</Button>
<Menu
open=this.state.open
onRequestClose=() => this.setState( open: false )
>
<MenuItem label="Home" />
<MenuItem label="Sign in" />
</Menu>
</div>
);
这个测试失败了,即使它应该可以直观地工作:
it('renders some menu items', () =>
const wrapper = mount(<AppMenu />);
expect(wrapper).toContainReact(<MenuItem label="Home" />);
);
这是 Jest 的失败输出:
renders some menu items
Expected <AppMenu> to contain <withStyles(MenuItem) className="MenuItem" component=... to=...>Home</withStyles(MenuItem)> but it was not found.
html Output of <AppMenu>:
<div><button tabindex="0" class="MuiButtonBase-root-3477017037 MuiButton-root-3294871568 MuiButton-flatContrast-53993421" type="button" role="button" aria-owns="simple-menu" aria-haspopup="true"><span class="MuiButton-label-49836587">Menu</span><span class="MuiTouchRipple-root-3868442396"></span></button><!-- react-empty: 5 --></div>
如您所见,就像渲染的只是<Button>
。事实上,当您在浏览器中渲染上述组件,并展开菜单并检查它的菜单项元素时,它们会在 DOM 中的其他地方渲染,而不是在按钮出现的位置内或什至附近。它们实际上是在文档的 <body>
元素下的 div <body><div data-mui-portal="true"> ... </div>
中呈现的。
那么如何测试这个菜单内容呢?
【问题讨论】:
【参考方案1】:Menu
在状态改变之前不会被渲染,所以你可以模拟点击Button
,让它的处理程序setState
,触发重新渲染,然后找到具体的MenuItem
。
此外,这可能无需完全安装即可完成:
it('renders some menu items', () =>
const wrapper = shallow(<AppMenu />);
// find the Menu Button
const button = wrapper.findWhere(node => node.is(Button) && n.prop('children') === 'Menu');
// simulate a click event so that state is changed
button.simulate('click');
// find the Home MenuItem
const menuItem = wrapper.findWhere(node => node.is(MenuItem) && n.prop('label') === 'Home');
// make sure it was rendered
expect(menuItem.exists()).toBe(true);
);
【讨论】:
【参考方案2】:是的,这可能很棘手。问题有两个方面:
-
触发点击事件意味着您需要将测试设为异步函数
菜单项不在您的包装元素
<AppMenu />
中 - 正如您所指出的,它们位于 DOM 的其他位置。
对于菜单项,您需要找到它们实际所在的位置。打开菜单的按钮在您的包装元素中,但菜单和菜单项不会,因此您需要按角色获取菜单,然后您可以通过文本获取菜单中的项目。
这是我如何使用 React 测试库执行此操作的示例。
import React, ReactElement from "react";
import render, screen from "@testing-library/react";
import AppMenu from "./AppMenu";
import getByText, fireEvent, getByLabelText from "@testing-library/react";
test("It renders some menu items", async () =>
const container = render(<AppMenu />);
const button = getByText(container, "Menu");
fireEvent.click(button);
const menuItem = screen.getByRole("menu");
expect(await getByLabelText(menuItem, "Home")).toBeTruthy();
expect(await getByLabelText(menuItem, "Sign in")).toBeTruthy();
);
【讨论】:
以上是关于React、Jest 和 Material-UI:如何测试以模态或弹出框呈现的内容的主要内容,如果未能解决你的问题,请参考以下文章
酶不模拟 React Material-UI v1 上的更改事件 - 选择组件
ThemeProvider 的 Jest Manual Mock