React Native测试中已await act仍触发未await警告,求原因解析
问题:已用await包裹act,仍出现"未await异步act调用"警告
场景与代码
待测试的自定义Hook
export function useSomethingSaver() { let somethingMutation = useMutation(async (newSomething) => { return await saveSomething(newSomething) }) return { saveSomething: somethingMutation.mutate, saveSomethingAsync: somethingMutation.mutateAsync, isLoading: somethingMutation.isLoading, isError: somethingMutation.isError, } }
测试代码
import { act, renderHook, waitFor } from '@testing-library/react-native' ... it('Something can be saved', async () => { saveSomething.mockImplementation(() => SOMETHING_DATA) const wrapper = ({ children }) => ( <BaseContexts queryClient={queryClient}>{children}</BaseContexts> ) const { result } = renderHook(() => useSomethingSaver(), { wrapper }) await act(async () => { await result.current.saveSomething(SOMETHING_SAVE_DATA) }) await waitFor(() => !result.current.isLoading) expect(saveSomething).toBeCalledWith(SOMETHING_SAVE_DATA) })
控制台警告信息
console.error Warning: You called act(async () => ...) without await. This could lead to unexpected testing behaviour, interleaving multiple act calls and mixing their scopes. You should - await act(async () => ...); at printWarning (node_modules/react/cjs/react.development.js:209:30) at error (node_modules/react/cjs/react.development.js:183:7) at node_modules/react/cjs/react.development.js:2559:15 at tryCallOne (node_modules/promise/lib/core.js:37:12) at node_modules/promise/lib/core.js:123:15 at flush (node_modules/asap/raw.js:50:29)
补充的act语句(仍出现警告)
const { result } = renderUseSomethingSaver(); await act(async () => { await result.current.saveSomethingAsync(SOMETHING_SAVE_DATA); }); await waitFor(() => !result.current.isLoading);
问题原因分析
1. 针对saveSomething(mutate)的情况
saveSomething对应React Query的mutate方法,它本身是同步执行的,不会返回Promise。你在act回调里await result.current.saveSomething(...)完全无效——这个await会立即完成,act提前结束,但React Query内部的异步请求和状态更新(比如isLoading的变化)是在act执行完毕后才触发的,这部分状态更新没有被act包裹,React就会抛出警告(提示文案有歧义,实际不是你没await act,而是act范围没覆盖到后续的状态更新)。
2. 针对saveSomethingAsync(mutateAsync)的情况
mutateAsync返回Promise,你await它确实会等待请求完成,但React Query的状态更新(比如isLoading变为false)可能是在Promise resolve之后才触发的,这部分更新同样不在你手动写的act范围内,因此警告依然出现。
3. Testing Library的内置处理逻辑
Testing Library的renderHook和waitFor已经内置了act的处理,手动包裹act反而会导致状态更新的范围不匹配。
修正后的测试代码
不需要手动用act包裹异步操作,直接调用方法后用waitFor等待状态变化即可:
it('Something can be saved', async () => { saveSomething.mockImplementation(() => SOMETHING_DATA) const wrapper = ({ children }) => ( <BaseContexts queryClient={queryClient}>{children}</BaseContexts> ) const { result } = renderHook(() => useSomethingSaver(), { wrapper }) // 直接调用异步方法,无需手动act包裹 await result.current.saveSomethingAsync(SOMETHING_SAVE_DATA); // waitFor会自动用act包裹状态更新的等待逻辑 await waitFor(() => expect(result.current.isLoading).toBe(false)); expect(saveSomething).toBeCalledWith(SOMETHING_SAVE_DATA) })
如果用同步的mutate方法:
it('Something can be saved', async () => { saveSomething.mockImplementation(() => SOMETHING_DATA) const wrapper = ({ children }) => ( <BaseContexts queryClient={queryClient}>{children}</BaseContexts> ) const { result } = renderHook(() => useSomethingSaver(), { wrapper }) // 调用同步的mutate方法 result.current.saveSomething(SOMETHING_SAVE_DATA); // 用waitFor等待状态更新,内部自动处理act await waitFor(() => expect(result.current.isLoading).toBe(false)); expect(saveSomething).toBeCalledWith(SOMETHING_SAVE_DATA) })
内容的提问来源于stack exchange,提问作者rKatex
相关产品推荐
相关产品推荐

