使用@testing-library/react-hooks测试异步React Hook时act的正确位置
看起来你遇到的问题根源在于mock的异步函数没有返回Promise,导致测试里的异步流程没有被@testing-library/react-hooks正确捕获,进而触发了act警告。咱们一步步来修正:
第一步:修正API的Mock方式
你的getDay应该是一个返回Promise的异步函数,但现在你用mockReturnValue直接返回了普通对象,这会让await getDay()同步完成,状态更新的时机脱离了测试工具的act包裹范围。把它改成mockResolvedValue来返回一个Promise:
jest.mock("../api/day", () => ({ getDay: jest.fn().mockResolvedValue({ some: "value" }) }));
第二步:简化测试代码(不需要手动包裹act)
@testing-library/react-hooks提供的renderHook和waitForNextUpdate已经内部帮你处理了act的包裹工作,所以你原来的测试逻辑其实是对的,只要修正mock就能解决警告。修正后的完整测试代码:
import { renderHook, act } from "@testing-library/react-hooks"; import { useDay } from "./day"; jest.mock("../api/day", () => ({ getDay: jest.fn().mockResolvedValue({ some: "value" }) })); describe.only("model/day", () => { it("returns data", async () => { const { result, waitForNextUpdate } = renderHook(() => useDay()); await waitForNextUpdate(); expect(result.current).toEqual({ some: "value" }); }); });
为什么之前会触发警告?
当你用mockReturnValue返回普通对象时,await getDay()会立即执行完成,状态更新set(day)会在测试工具的act处理流程之外触发,React就会抛出那个警告。而改成mockResolvedValue后,异步流程会被正确识别,waitForNextUpdate会等待Promise resolve并完成状态更新,整个过程都被包裹在act里,警告自然就消失了。
另外,如果你真的需要手动包裹act(比如处理某些同步状态更新),正确的方式是把触发状态更新的逻辑放在act回调里,但在这个异步Hook的场景下,waitForNextUpdate已经足够处理了,不需要额外手动操作。
内容的提问来源于stack exchange,提问作者Edgar Friendly

