React,Jest和Material-UI:如何测试以模式或弹出方式呈现的内容

Ern*_*sto 7 testing reactjs jestjs material-ui enzyme

有一些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>
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

即使此测试应直观地起作用,它也会失败:

it('renders some menu items', () => {
  const wrapper = mount(<AppMenu />);
  expect(wrapper).toContainReact(<MenuItem label="Home" />);
});
Run Code Online (Sandbox Code Playgroud)

这是杰斯特失败的输出:

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>
Run Code Online (Sandbox Code Playgroud)

如您所见,就好像渲染的全部是<Button>。确实,当您在浏览器中呈现上述组件,并展开菜单并检查其菜单项元素时,它们将呈现在DOM中的其他位置,而不是在按钮出现的位置内或附近。实际上,它们是在<body><div data-mui-portal="true"> ... </div>文档<body>元素正下方的div中呈现的。

那么如何测试此菜单内容?

小智 5

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

  1. 触发点击事件意味着你需要让你的测试成为一个异步函数
  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();
});
Run Code Online (Sandbox Code Playgroud)


Ken*_*ory 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);
});
Run Code Online (Sandbox Code Playgroud)