【问题标题】:React/Jest/MSW Wait for response throws errors using act()React/Jest/MSW 使用 act() 等待响应抛出错误
【发布时间】:2022-02-02 12:24:00
【问题描述】:

目前,我正在学习如何使用 React 进行单元测试。但是,我想用 TypeScript 来学习它,所以课程没有涵盖 TS 中出现的大多数错误。

我有一个使用 Mock Service Worker (msw) 配置的简单测试功能:

fit("sends username, email and password to backend after clicking the button", async () => {
  let requestBody;
  const server = setupServer(
    rest.post("/api/1.0/users", (req, res, ctx) => {
      requestBody = req.body;
      return res(ctx.status(200));
    })
  );
  server.listen();

  setupAll(); // Gets the elements on the page (like button)

  userEvent.click(button);

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

  expect(requestBody).toEqual({
    username: "LegacyUser",
    email: "legacy@user.com",
    password: "P455w0rd!",
  });
});

理论上,这“有效”(它在测试列表中显示为通过),但上面会出现错误,例如:

console.error
  Warning: An update to SignUpPage 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 */

所以当我像act(() => userEvent.click(button)) 一样包装userEvent.click(button) 时,它一直显示此错误消息。

点击 userEvent 触发 onSubmit 处理程序:

const formSubmitHandler = (event: React.FormEvent<HTMLFormElement>) => {
  event.preventDefault();

  const { username, email, password } = formEntries;
  setIsApiInProgress(true);
  axios
    .post("/api/1.0/users", {
      username,
      email,
      password,
    })
    .then((response) => {
      if (response.status >= 200 && response.status < 300) {
        setIsSignUpSuccessfull(true);
        // console.log("OK", response.data);
        return response.data;
      }
      throw new Error(response.status.toString());
    })
    .catch(({ response }) => {
      console.log("CATCH", response);
    })
    .finally(() => {
      setIsApiInProgress(false);
    });
};

我在这里做错了什么?另外,有没有办法在不使用超时的情况下等待已解决的承诺?这种方式感觉有点hacky。

【问题讨论】:

    标签: reactjs typescript axios jestjs msw


    【解决方案1】:

    这不是 Typescript 相关问题。您需要将这一行 await new Promise((resolve) =&gt; setTimeout(resolve, 250)); 包含在 act 中,即 await act(() =&gt; new Promise((resolve) =&gt; setTimeout(resolve, 250)))。你可以阅读更多关于它的信息here

    与您的问题最相关的是这部分:

    所以来自 React 的行为警告是告诉我们一些事情 当我们没想到会发生任何事情时,我们的组件发生了 发生。所以你应该包装你所做的每一次互动 你的组件让 React 知道我们期望我们的组件 执行一些更新,当你不这样做并且有 更新时,React 会警告我们发生了意外更新。这个 帮助我们避免上述错误。

    对你和我来说幸运的是,React 会自动为任何 你在 React 调用栈中运行的代码(比如点击事件) 其中 React 调用你的事件处理程序代码来更新 组件),但它无法处理在外部运行的任何代码 它是自己的调用堆栈(例如由于 解决了您正在管理的承诺,或者您正在使用开玩笑的假货 计时器)。在这些情况下,您通常需要包装 自己在 act(...) 或 async act(...) 中。但是,反应测试 库具有自动包装在 act 中的异步实用程序!

    注意它是如何特别提到如何为您处理点击事件的(因此为什么包装 userEvent 并没有解决问题)您自己管理的承诺没有得到处理(因此为什么我们我们自己把它包装起来)。

    你是对的,这是一种 hacky 方式。您可以使用 RTL 的异步方法,这些方法在他们的 docs 中有很好的解释。

    这个:

    await new Promise((resolve) => setTimeout(resolve, 250));
    
      expect(requestBody).toEqual({
        username: "LegacyUser",
        email: "legacy@user.com",
        password: "P455w0rd!",
      });
    

    变成这样:

    await waitFor(() => {
      expect(requestBody).toEqual({
        username: "LegacyUser",
        email: "legacy@user.com",
        password: "P455w0rd!",
      });
    })
    
    

    【讨论】:

      猜你喜欢
      • 2020-12-12
      • 1970-01-01
      • 2021-04-04
      • 1970-01-01
      • 2019-07-11
      • 2021-01-12
      • 2020-10-30
      • 2021-07-17
      • 2018-11-07
      相关资源
      最近更新 更多