首页 > 解决方案 > 使用 React 测试库在 React 中断言表单提交的 prop 调用

问题描述

您可以在以下代码框中重现该问题:

编辑持续的 Meme 传递

作为示例,我制作了一个组件,该组件使用以下代码在每次单击表单按钮时获取随机图像:

import { Button } from "antd";
...
class ContinuousMemeDeliveryApi extends React.Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }

  static defaultProps = { getImage };
  state = { image: images[0], random: 0 };

  handleClick = async e => {
    e.preventDefault();
    const { data } = await this.props.getImage();
    this.setState({ image: data.image, random: data.random });
  };
  ...
  render() {
    return (
      <form onSubmit={this.handleClick}>
        <Button htmlType="submit">Click Me</Button>
        ...
      </form>
    );
  }

这按预期工作,我制作了以下代码来测试实现:

test("loads random memes on click", async () => {
  const mockGetImage = jest.fn(() =>
    Promise.resolve({ data: { image: "testImage.jpg" } })
  );
  const { getByText } = render(
    <ContinuousMemeDeliveryApi getImage={mockGetImage} />
  );

  const clickMeButton = getByText(/click/i);

  fireEvent.click(clickMeButton);
  // @TODO: fix assertion
  expect(mockGetImage).toHaveBeenCalledTimes(1);
});

但是,测试失败并显示以下错误消息:

expect(jest.fn()).toHaveBeenCalledTimes(1)

Expected mock function to have been called one time, but it was called zero times.

关于为什么失败的任何想法?

标签: javascriptreactjsunit-testingjestjsreact-testing-library

解决方案


由于您在同一个文档(您的应用程序和测试)中有 2 个反应根渲染,因此您不能将默认值document.body用作反应测试库的基本元素。

使用当前的方法,您的查询实际上是在您的应用程序中而不是在呈现的测试中找到元素,因为它们是绑定的 baseElement = document.body并且您div#container是第一位的。这就是为什么根本不调用 mock 的原因。

工作示例

您可以添加额外的容器来隔离测试 DOM 树

// index.html
<div id="test-container"></div>

现在您可以在渲染测试时指定容器

import { render as rtlRender, fireEvent, wait } from "react-testing-library";

// get the container
const container = document.getElementById('test-container')

// override render to provide custom container
const render = (ui, options = {}) => rtlRender(ui, { container, ...options })

推荐阅读