【问题标题】:How correctly write Jest test for avoid act() warning?如何正确编写 Jest 测试以避免 act() 警告?
【发布时间】:2025-12-31 05:40:10
【问题描述】:

useEffect 通常使用 Promises 来更新状态。

此更新会导致长时间警告:Warning: An update to null inside a test was not wrapped in act(...).

对于这种情况如何正确编写 Jest Test?

活生生的例子,可重现的例子:

https://codesandbox.io/s/jest-test-for-useeffect-with-promises-spieq?file=/index.test.js

index.test.js

import React from "react";
import Hello from "./Hello";
import { create, act } from "react-test-renderer";

it("works", () => {
  let root;
  act(() => {
    root = create(<Hello />);
  });

  // console.log("From test:", );
  let repr = JSON.stringify(root.toJSON());
  expect(repr).toBe('{"type":"span","props":{},"children":["Hello! "]}');
});

你好.js

import React, { useState, useEffect } from "react";

export default () => {
  const [count, setCount] = useState();

  useEffect(() => {
    Promise.resolve({}).then(() => setCount(4));
  }, []);

  return <span>Hello! {count}</span>;
};

更新 1:

propasal 之一的结果相同:

【问题讨论】:

  • 请在您正在使用的相关代码的问题中包含minimal, complete, and reproducible code example 作为代码 sn-p。指向外部资源的链接往往会在一段时间后失效。代码应包含在您的问题中,供未来的读者阅读。
  • @DrewReese 我将链接附加到完全复制的沙盒

标签: javascript reactjs react-hooks jestjs react-test-renderer


【解决方案1】:

我建议你使用testing-library/react,它可以和jest一起顺利使用,并提供async methods

你的测试会变成这样:

import React from "react";
import Hello from "./Hello";
import { render } from '@testing-library/react';

it("works", () => {
   const { baseElement } = render(<Hello />);
   expect(baseElement).toBeTruthy();
});

编辑:为了完整起见,在您的示例中,您尝试测试组件中的更新是否在使用承诺的效果之后发生。

为了waitFor异步你可以使用方法findByfindBy方法是getBy查询和waitFor的组合。

import React from "react";
import Hello from "./Hello";
import { render, findByText } from "@testing-library/react";
import "@testing-library/jest-dom";

it("works", async () => {
  const { baseElement } = render(<Hello />);
  expect(await findByText(baseElement, "Hello! 4")).toBeVisible();
});

让我解释一下,import "@testing-library/jest-dom" 允许你使用方法toBeVisible

所以,这个测试正在渲染一个组件 Hello,然后它在 baseElement(它是组件本身)内等待(默认值,1000ms)寻找文本“Hello!4”,然后如果找到它将返回找到元素,我们将测试它是否可见。

【讨论】:

  • 感谢您的回答,但结果相同。您可以签入沙箱,我为您的示例添加屏幕截图。
  • @GulaevValentin 我认为问题应该集中在react-test-rendereruseEffect + Promise。 @testing-library/react 会起作用,但这并不能直接回答您的问题
  • @slideshowp2 如果将useEffect 中的承诺重写为async 函数,它会在没有警告的情况下工作。但问题是如何正确测试承诺风格。
【解决方案2】:

看起来这个示例工作正常

import React from "react";
import Hello from "./Hello";
import { render, screen, waitFor } from "@testing-library/react";
import "@testing-library/jest-dom";

it("works", () => {
  render(<Hello />);
  return waitFor(() => screen.getByText("Hello! 4"));
  // expect(baseElement.toBeTruthy();
});

【讨论】:

  • 我已经编辑了我的答案,以便更好地解释我想给你的建议
  • @FrancescoVattiato 通常你是对的。我只是避免使用 asyncawait 并以 Promise 风格编写。
最近更新 更多