首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >React/Jest/MSW使用act()等待响应抛出错误

React/Jest/MSW使用act()等待响应抛出错误
EN

Stack Overflow用户
提问于 2022-01-25 12:59:22
回答 1查看 2.5K关注 0票数 1

目前,我正在学习如何使用React进行单元测试。但是,我想用TypeScript来学习它,所以这门课程不涵盖TS发生的大多数错误。

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

代码语言:javascript
复制
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!",
  });
});

从理论上讲,这个“works”(它在测试列表中显示为Pass ),但在它之上会出现错误,例如:

代码语言:javascript
复制
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 */

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

单击userEvent触发onSubmit处理程序:

代码语言:javascript
复制
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);
    });
};

我在这里做错什么了?另外,是否有一种方法可以在不使用超时的情况下等待已解决的承诺?这种感觉有点烦人。

EN

回答 1

Stack Overflow用户

发布于 2022-02-02 04:24:00

这不是打字稿相关的问题。您需要将这一行await new Promise((resolve) => setTimeout(resolve, 250));包装在act中,即await act(() => new Promise((resolve) => setTimeout(resolve, 250)))。您可以阅读更多关于它的这里

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

因此,反应的警告是告诉我们,当我们没有预料到会发生什么事情时,我们的组件发生了一些事情。因此,您应该包装您与组件在行动中进行的每一次交互,让React知道我们的组件会执行一些更新,当您不这样做时,如果有更新,React会警告我们发生了意外的更新。这有助于我们避免像上面描述的那样的bug。 幸运的是,对您和我来说,React会自动处理在React调用堆栈中运行的任何代码(就像在事件处理程序代码中单击React调用来更新组件),但是它不能处理运行在它自己的调用堆栈之外的任何代码(就像异步代码因为您正在管理的解析承诺而运行,或者如果您使用的是开玩笑的假计时器)。在这种情况下,通常需要在行动中将其包装(.)或异步行为(.)你自己。但是,有自动包装在act中的异步实用程序!

请注意,它特别提到了如何为您处理单击事件(因此,为什么包装userEvent没有解决问题)、--如何处理您自己管理自己的承诺--(因此,我们自己包装它的原因)。

你说得对,这是一种讨厌的方式。您可以使用RTL的异步方法,这些方法在其文档中得到了很好的解释。

这是:

代码语言:javascript
复制
await new Promise((resolve) => setTimeout(resolve, 250));

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

变成这样:

代码语言:javascript
复制
await waitFor(() => {
  expect(requestBody).toEqual({
    username: "LegacyUser",
    email: "legacy@user.com",
    password: "P455w0rd!",
  });
})
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70849044

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档