React组件中多上下文自定义Hook的API调用测试及渲染问题排查
问题分析与解决方案
你遇到的result.current返回null的核心原因是wrapper组件没有正确渲染renderHook生成的测试组件。当前你的wrapper直接硬编码了<App />,但renderHook需要wrapper接受children属性并渲染它(这个children就是调用useMyHook的测试组件)。此外,测试时机和API调用的监听方式也需要调整。
核心修复步骤
1. 修正Wrapper组件
Wrapper必须接受children并将其包裹在上下文Provider中,这样renderHook生成的测试组件(调用useMyHook)才能获取到上下文:
const wrapper = ({ children }) => ( <Provider1 value={mockContext1Value}> {/* 需传入mock上下文值 */} <Provider2 value={mockContext2Value}> {children} {/* 渲染renderHook的测试组件 */} </Provider2> </Provider1> );
2. 正确监听API调用
如果callApi在Hook挂载时自动执行,直接spyresult.current.callApi会错过调用时机(因为spy是在Hook渲染后才添加的)。正确做法是mock底层API服务,而非spy返回的函数。
方案一:直接测试useMyHook
适合单独验证Hook的逻辑:
import { renderHook, waitForNextUpdate } from '@testing-library/react'; import useMyHook from './useMyHook'; import { Provider1 } from './context1'; import { Provider2 } from './context2'; // Mock底层API调用(根据实际实现调整) global.fetch = jest.fn().mockResolvedValue({ ok: true }); describe('useMyHook', () => { it('挂载时发起指定API调用', async () => { // 定义上下文的mock值 const mockContext1 = { id: 'test-id' }; const mockContext2 = { token: 'test-token' }; const wrapper = ({ children }) => ( <Provider1 value={mockContext1}> <Provider2 value={mockContext2}> {children} </Provider2> </Provider1> ); // 渲染Hook const { result } = renderHook(() => useMyHook(), { wrapper }); // 验证callApi是函数 expect(typeof result.current.callApi).toBe('function'); // 等待useEffect执行(API调用在挂载时触发) await waitForNextUpdate(); // 断言API被正确调用 expect(fetch).toHaveBeenCalledWith( `/api/${mockContext1.id}`, expect.objectContaining({ headers: { Authorization: mockContext2.token } }) ); }); });
方案二:集成测试App组件
如果你需要验证App组件使用Hook时的完整流程,直接渲染App更贴合实际场景:
import { render, waitFor } from '@testing-library/react'; import App from './App'; import { Provider1 } from './context1'; import { Provider2 } from './context2'; // Mock底层API调用 global.fetch = jest.fn().mockResolvedValue({ ok: true }); describe('App', () => { it('通过useMyHook发起API调用', async () => { const mockContext1 = { id: 'test-id' }; const mockContext2 = { token: 'test-token' }; // 渲染App及上下文Provider render( <Provider1 value={mockContext1}> <Provider2 value={mockContext2}> <App /> </Provider2> </Provider1> ); // 等待API调用完成 await waitFor(() => expect(fetch).toHaveBeenCalled()); // 验证调用参数 expect(fetch).toHaveBeenCalledWith( `/api/${mockContext1.id}`, expect.objectContaining({ headers: { Authorization: mockContext2.token } }) ); }); });
内容的提问来源于stack exchange,提问作者Yamo93
相关产品推荐
相关产品推荐

