使用 React 测试库在 React 中断言表单提交的道具调用

Asserting prop call for form submit in React using React Testing Library

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

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

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.

关于失败原因的任何想法?

由于您在同一文档(您的应用程序和测试)中有 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 })