如何使用React-testing-library测试依赖自定义Hook的React组件?
问题原因
这个act(...)警告的本质是:你触发表单提交后,自定义Hook内的fetchData是异步函数,内部的状态更新(setLoading、请求结束后的setResponse/setError/setLoading(false))都属于微任务,会在测试用例的同步代码(也就是你写的expect断言)执行完成后才运行。React检测到组件在测试预期流程外发生了状态更新,因此抛出警告。
额外提一个你代码中的显性bug:自定义useMakeAsyncCall返回的加载状态字段是loading,但业务组件中解构写的是isLoading,会导致加载状态始终为undefined,需要先修正:
const { error, loading, fetchData } = useMakeAsyncCall({ asyncFunctionToRun: () => signUpUser(), runOnMount: false, });
修复方案
第一步:Mock异步请求
先Mock掉Firebase的signUpUser请求,避免真实请求发送,同时方便控制请求的返回状态:
// 示例,替换为你实际的signUpUser导入路径 jest.mock('./path/to/auth.js', () => ({ signUpUser: jest.fn().mockResolvedValue({ json: () => Promise.resolve({ success: true }) }) }));
第二步:等待异步更新完成后再执行断言
React Testing Library提供的异步工具已经内置了act处理,不需要手动包裹act,两种常用方案按需选择:
方案1:用waitFor包裹断言(通用场景)
waitFor会等待内部异步逻辑执行完成后再运行断言,自动处理act包裹:
import { render, fireEvent, waitFor } from '@testing-library/react'; it('does things', async () => { const { container, getByTestId } = render(<Component/>); const form = getByTestId('form'); fireEvent.submit(form); await waitFor(() => { expect(container.firstChild).toMatchSnapshot(); }); });
方案2:用findBy*查询(需要匹配元素场景)
findBy*系列查询自带等待功能,本身也会把状态更新包裹在act内,适合测试加载态、成功/失败提示这类场景:
it('shows loading state after form submit', async () => { const { getByTestId, findByText } = render(<Component/>); const form = getByTestId('form'); fireEvent.submit(form); // 自动等待加载文案出现 const loadingTip = await findByText('提交中...'); expect(loadingTip).toBeInTheDocument(); });
可选:单独测试自定义Hook
如果你需要单独测试useMakeAsyncCall的逻辑,可以使用@testing-library/react-hooks的renderHook工具配合waitFor测试,同样不会出现act警告。
内容的提问来源于stack exchange,提问作者Person
相关产品推荐
相关产品推荐

