【问题标题】:Can't close a modal menu through enzyme无法通过酶关闭模式菜单
【发布时间】:2018-12-18 17:42:41
【问题描述】:

问题很简单。我有一个我想测试的 material-ui 菜单适当地关闭(通过用户在菜单外单击)。

事实证明,这非常具有挑战性。这是组件(从the docs 撕下来):

class App extends React.Component {
  state = {
    anchorEl: null
  };

  handleClick = event => {
    this.setState({ anchorEl: event.currentTarget });
  };

  handleClose = () => {
    this.setState({ anchorEl: null });
  };

  render() {
    const { anchorEl } = this.state;

    return (
      <div>
        <Typography variant="body1"> Click Me</Typography>
        <Button
          aria-owns={anchorEl ? "simple-menu" : undefined}
          aria-haspopup="true"
          onClick={this.handleClick}
        >
          Open Menu
        </Button>
        <Menu
          id="simple-menu"
          anchorEl={anchorEl}
          open={Boolean(anchorEl)}
          onClose={this.handleClose}
        >
          <MenuItem onClick={this.handleClose}>Profile</MenuItem>
          <MenuItem onClick={this.handleClose}>My account</MenuItem>
          <MenuItem onClick={this.handleClose}>Logout</MenuItem>
        </Menu>
      </div>
    );
  }
}

还有测试:

const props = {
  classes: {}
};

describe("test", () => {
  it("closes the menu", () => {
    const wrapper = mount(<App {...props} />);
    wrapper.find(Button).simulate("click");
    expect(wrapper.find(Menu).prop("open")).toBe(true);
    wrapper.find(Typography).simulate("click");
    expect(wrapper.find(Menu).prop("open")).toBe(false);
    wrapper.unmount();
  });

  it("closes the menu a different way", () => {
    const outerNode = document.createElement("div");
    outerNode.setAttribute("id", "root-node");
    document.body.appendChild(outerNode);
    const wrapper = mount(<App {...props} />, {
      attachTo: outerNode
    });
    wrapper.find(Button).simulate("click");
    expect(wrapper.find(Menu).prop("open")).toBe(true);
    outerNode.click();
    expect(wrapper.find(Menu).prop("open")).toBe(false);
    wrapper.unmount();
  });

  it("closes the menu yet a different way", () => {
    const eventMap = {};
    window.addEventListener = jest.fn((event, cb) => {
      eventMap[event] = cb;
    });
    const wrapper = mount(<App {...props} />);
    wrapper.find(Button).simulate("click");
    expect(wrapper.find(Menu).prop("open")).toBe(true);
    wrapper.find(Typography).simulate("click");
    eventMap.click();
    expect(wrapper.find(Menu).prop("open")).toBe(false);
    wrapper.unmount();
  });
});

很遗憾,这些测试都不起作用。我正在做一些挖掘,发现this SO post 链接了this github issue 解决方案和第三种方法最初来自的地方,但是,我无法让其中任何一个工作。

我用下面的代码创建了一个 CodeSandbox,供这里任何愿意提供帮助的慷慨人士使用。

【问题讨论】:

  • 我认为这不是您应该编写单元测试的方式。你应该只测试你的代码。在这里,您正在尝试测试 material-ui 代码。你应该只测试你的 onClose 函数是否应该将 isOpen 状态设置为 false 并且 rest 将由 material-ui 处理,因为他们已经在他们的代码中测试了他们的逻辑。
  • 我同意,但是,我喜欢测试用户如何测试/使用应用程序。我想触发关闭菜单不是通过手动触发onClose,而是通过以onClose被调用的方式与ui交互。这也确保onClose 正确连接到元素。
  • 这可能是另一种保证onClose 在没有模拟onClose 的情况下被调用的方式,因为这样做的结果是关闭菜单。无论哪种方式,它都不成功。

标签: javascript reactjs jestjs enzyme


【解决方案1】:

这可能是因为点击后元素没有立即出现。

你能试试 - 酶异步助手,

等待waitForElement(wrapper, Menu);

编辑:

在深入研究了 simulation() 方法和 Menu 组件之后,我的发现如下:

  • simulate('click') 在内部查找正在模拟的组件上的 'onClick' 属性,如果存在则调用该方法,否则不会执行任何操作。在您的情况下,Typography 没有任何 onClick 处理程序,因此它不会做任何事情。即使它在那里,它也会调用您传入的函数,而不是 Menu 的实际内部实现,它会在此处关闭。
  • 内部使用Backdrop组件的菜单,该组件具有onClick方法并在您单击外部时关闭菜单。

您的问题的解决方案是:

   wrapper.find(Backdrop).simulate("click");

还有导入:

import { Button, Menu, Typography, Backdrop } from "@material-ui/core";

您可以在此处找到运行示例:https://codesandbox.io/s/vyon4kvjpy

【讨论】:

  • 更新了答案。请检查一次。
  • 我得到“方法“模拟”意味着在 1 个节点上运行。找到了 0 个节点。”我不相信 Backdrop 元素在 DOM 中。
  • 非常好!我正在查看错误的测试用例:P 谢谢@Rakesh!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-06
  • 2013-12-03
  • 1970-01-01
  • 2014-07-21
  • 2015-11-25
相关资源
最近更新 更多