使用 userEvent.click 错误的 act() 警告响应测试库

BSK*_*BSK 8 reactjs jestjs react-testing-library

我有几个用 Jest 和 React 测试库编写的测试。它们都模拟获取并使用 userEvent.click 调用来触发发出获取请求的提交按钮。组件中的状态得到更新,我做出断言。我正在使用 useEffect 挂钩来填充数据数组,但它仅在初始加载时运行,因为我向它传递了一个空的依赖项数组。目前我所有的测试都通过了。如果我一起运行它们,我会得到一个错误的 act() 错误,该错误源于 useEffect:

Warning: It looks like you're using the wrong act() around your test interactions.
Be sure to use the matching version of act() corresponding to your renderer:

// for react-dom:
import {act} from 'react-dom/test-utils';
// ...
act(() => ...);

// for react-test-renderer:
import TestRenderer from react-test-renderer';
const {act} = TestRenderer;
// ...
act(() => ...);
Run Code Online (Sandbox Code Playgroud)

但是,当我单独运行其中一个时,我没有收到警告。我可以单独运行其中任何一个,并且不会收到任何警告。仅当我同时运行两个或多个测试时,我才会收到警告。

我的测试是:

Warning: It looks like you're using the wrong act() around your test interactions.
Be sure to use the matching version of act() corresponding to your renderer:

// for react-dom:
import {act} from 'react-dom/test-utils';
// ...
act(() => ...);

// for react-test-renderer:
import TestRenderer from react-test-renderer';
const {act} = TestRenderer;
// ...
act(() => ...);
Run Code Online (Sandbox Code Playgroud)

我读到您不必将 userEvent 包装在 act() 中,因为它已经在幕后。但是,如果我不将其包装在行动中,我的测试将失败并抛出:

Warning: An update to CartDetail inside a test was not wrapped in act(...).
    
When testing, code that causes React state updates should be wrapped into act(...):
    
act(() => {
  /* fire events that update state */
});
/* assert on the output */
Run Code Online (Sandbox Code Playgroud)

即使我注释掉我的断言,我的测试也会通过(当然),但我仍然收到错误的 act() 警告。问题直接来自:

describe("CartDetail", () => {
  test("Order is submitted when user clicks Place Order button.", async () => {
    global.fetch = jest.fn().mockImplementationOnce(() =>
      Promise.resolve({
        status: 200,
      })
    );
    
    renderComponent();

    await act(async () => {
      userEvent.click(await screen.findByRole("button", { name: "Place Order" }));
    });

    expect(screen.queryByText("Your meal order was successfully processed.")).toBeInTheDocument();
  });

  test("Error message is displayed to user when order fails with a 400.", async () => {
    global.fetch = jest.fn().mockImplementationOnce(() =>
      Promise.resolve({
        status: 400,
      })
    );
    
    renderComponent();

    await act(async () => {
      userEvent.click(await screen.findByRole("button", { name: "Place Order" }));
    });

    expect(screen.queryByText("Please confirm that you are ordering at least one of each meal you have in your cart.")).toBeInTheDocument();
    userEvent.click(screen.getByLabelText("Close alert"));
  });

  test("Error message is displayed to user when API fails.", async () => {
    global.fetch = jest.fn().mockRejectedValueOnce(() =>
      Promise.reject({
        status: 500,
      })
    );
    
    renderComponent();

    await act(async () => {
      userEvent.click(await screen.findByRole("button", { name: "Place Order" }));
    });

    expect(screen.queryByText("Your order failed.")).toBeInTheDocument();
    userEvent.click(screen.getByLabelText("Close alert"));
  });
});
Run Code Online (Sandbox Code Playgroud)

我不明白当 useEffect 在初始加载时执行并且不再运行时,问题是如何源于 useEffect 的,包括通过 userEvent.click() 单击按钮时。我尝试使用 waitFor() 代替,并产生相同的精确结果。我在互联网上搜索过,没有什么比这更让我接近的了。这个 GitHub 线程提到这是一个已知问题,但现在有点旧了,所以我不知道它是否仍然有效。

Dan*_*ñoz 7

您可能只需要等待click电话即可。

await userEvent.click(await screen.findByRole("button", { name: "Place Order" });
Run Code Online (Sandbox Code Playgroud)

在版本14方法中,例如clicktype返回一个Promise. 那是版本不同13


BSK*_*BSK 2

我终于找到了解决方法。这篇 GitHub 帖子提到了我已经完成的包装userEvent()调用。act()控制台错误的解决方法是在调用之后和断言之前Warning: It looks like you're using the wrong act() around your test interactions.添加以下代码:userEvent()

await new Promise((resolve) => setTimeout(resolve, 0));
Run Code Online (Sandbox Code Playgroud)

我更新的测试现在看起来像这样:

test("Order is submitted when user clicks Place Order button.", async () => {
    global.fetch = jest.fn().mockImplementationOnce(() =>
      Promise.resolve({
        status: 200,
      })
    );
    
    renderComponent();

    await act(async () => {
      userEvent.click(await screen.findByRole("button", { name: "Place Order" }));
    });

    await new Promise((resolve) => setTimeout(resolve, 0));

    expect(screen.queryByText("Your meal order was successfully processed.")).toBeInTheDocument();
  });
Run Code Online (Sandbox Code Playgroud)

错误的 act() 警告现在消失了。