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 组件不会将其结果呈现在与其父级放置的组件相同的位置。其中我们有DialogMenu等。

这使得在 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>

如您所见,就像渲染的只是&lt;Button&gt;。事实上,当您在浏览器中渲染上述组件,并展开菜单并检查它的菜单项元素时,它们会在 DOM 中的其他地方渲染,而不是在按钮出现的位置内或什至附近。它们实际上是在文档的 &lt;body&gt; 元素下的 div &lt;body&gt;&lt;div data-mui-portal="true"&gt; ... &lt;/div&gt; 中呈现的。

那么如何测试这个菜单内容呢?

【问题讨论】:

【参考方案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】:

是的,这可能很棘手。问题有两个方面:

    触发点击事件意味着您需要将测试设为异步函数 菜单项不在您的包装元素 &lt;AppMenu /&gt; 中 - 正如您所指出的,它们位于 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

如何在 Enzyme / Jest Test 中切换和检查 Material UI 复选框

Theme.spacing 不是函数

使用 React 和 Material-UI 的开源项目?

如何覆盖(覆盖)material-ui(React)中的类和样式