开发者问题收集

如何使用 React Testing Library 和 Jest 测试必填输入字段?

2021-06-20
20042

我尝试使用 React Testing Library 和 Jest 通过测试弹出窗口是否存在来测试必填输入字段,但失败了。我尝试了几种变体,但都不起作用。 UI 中的步骤如下:

  1. 单击空字段 空选择字段
  2. 单击字段旁边(模糊)
  3. 将鼠标悬停在字段上
  4. 获取所需消息 所需消息

实现此目的的测试代码是:

const input = screen.getByRole('textbox', {name: /name \*/i})
  expect(input).toBeTruthy();
  fireEvent.click(input);
  fireEvent.blur(input);
  await waitFor(() => expect(input).toHaveValue(config.EMPTY_STRING));

  act(() => {
    fireEvent.mouseOver(input);
  });

  await waitFor(() => {
    expect(screen.getByText('Name is required')).toBeTruthy();
  });

不幸的是,它不起作用,我收到此错误: TestingLibraryElementError:无法找到包含文本的元素:名称是必需的。这可能是因为文本被多个元素分解了。在这种情况下,您可以为文本匹配器提供一个函数,以使匹配器更加灵活。

我以这种方式更改了最后一行: expect(screen.getByText('Name is required')).toBeInTheDocument(); ,但得到了相同的错误。

我尝试使用 expect(screen.findByText('Name is required')).toBeInTheDocument(); ,但得到了相同的错误。

我最后一次尝试的是: expect(screen.findByText('Name is required')).toBeTruthy(); 。此处字段的测试通过,但整体测试失败。我得到的错误是: console.error 警告:您似乎有重叠的 act() 调用,这不受支持。在进行新的 act() 调用之前,请务必等待之前的 act() 调用。 错误:未捕获 [TypeError:无法读取 null 的属性“useRealTimers”]

所以我被卡住了。任何帮助都将不胜感激。非常感谢!

3个回答

jest-dom 现在具有 toBeValid toBeInvalid 自定义匹配器。

it("should show the validity", async () => {
  let { user } = prep(<ComponentToTest />)
  let requiredRadioOption = screen.getByLabelText("radio-option-0")

  expect(requiredRadioOption).toBeInvalid()

  await user.click(requiredRadioOption)

  expect(requiredRadioOption).toBeValid()
})
Christian Todd
2022-05-10

截至撰写本文时,我认为这是不可能的,因为 HTML5 验证的弹出窗口是由浏览器处理的。

Jest 是一个基于 Node 的运行器。create-react-app docs 对此进行了最好的描述:

Jest is a Node-based runner. This means that the tests always run in a Node environment and not in a real browser. This lets us enable fast iteration speed and prevent flakiness.

While Jest provides browser globals such as window thanks to jsdom, they are only approximations of the real browser behavior. Jest is intended to be used for unit tests of your logic and your components rather than the DOM quirks.

We recommend that you use a separate tool for browser end-to-end tests if you need them.

如果您想测试弹出窗口,可以使用合适的工具,例如 Cypress。您可以参考 这个

^ 这是我使用的方法。然后在 testing-library 中,我测试“ 我的工作 ”,即

Actually triggering the browser-implementation of the validation is not really testing your work (but instead testing the browser itself), which might be nice but in my opinion is unnecessary.

因此,在您的示例中,由于需要输入,并且您期望弹出窗口出现,因此可以假设输入的值仍然为空。因此,我们为此编写了一个断言:

expect(input).toHaveValue("")
// I also add some css to highlight the required input,
// so I assert on that
expect(input).toHaveClass("is-required")
neldeles
2021-12-29

您可以使用 querySelector 和伪类选择器通过 React Testing Library 测试 HTML5 内置验证:

:valid

示例:

import { render, screen } from "@testing-library/react";
import user from "@testing-library/user-event";
import Client from "./index";

const passedBrowserValidation = (view, name) => {
  const selector = `[name=${name}]:valid`;
  // eslint-disable-next-line testing-library/no-node-access
  const isValidated = view.container.querySelector(selector);
  return Boolean(isValidated);
};

const renderViewNew = () => {
  return render(
    <Client />
  );
};

const getClientName = () => {
  return screen.findByRole('textbox', { name: /Name/i });
};

describe("Client form", () => {
  it("Should validate ok a right value in name", () => {
    const view = renderViewNew();
    const nameField = getClientName();
    user.type(nameField, "Acme");
    expect(passedBrowserValidation(view, "name")).toBeTruthy();
  });
});

请注意,Testing Library 作者试图 阻止 使用 querySelector ,但在这种情况下,这可能是合理的。这就是 linter 注释的原因: // eslint-disable-next-line testing-library/no-node-access

Johann Echavarria
2022-03-17