如何测试指定ApolloClient实例的useLazyQuery钩子?
问题:指定ApolloClient实例的useLazyQuery测试Mock不生效
在测试使用指定ApolloClient实例的useLazyQuery钩子时,遇到Mock数据无法被组件接收的问题:移除钩子中的client配置后测试正常,但业务逻辑依赖该特定客户端。
业务代码(useLazyQuery钩子)
const [ getSpecificReport, { loading: contentLoading, error: contentError, data: content }, ] = useLazyQuery<SpecificReportResponse>( SPECIFIC_REPORT(testResultsData?.getTestResults.testType), { client: cmsClient, // 特定的ApolloClient实例 fetchPolicy: "network-only", onCompleted: () => { setScreenData(); }, onError: (err) => { // 省略错误处理逻辑 }, } );
Mock配置
const mocks = [{ request: { query: SPECIFIC_REPORT('Report Title'), fetchPolicy: 'network-only', }, result: { data: { allReports: [getReportTestData()], } as SpecificReportResponse, }, }]
测试代码
... const { getByText, getAllByText } = render( <MockedProvider mocks={mocks} addTypename={false} > <ResultsScreen {...mockProps} /> </MockedProvider> ); await waitFor(() => { new Promise((resolve) => setTimeout(resolve, 3000)); expect(getByText(/Something/)).toBeTruthy(); // 省略其他断言 } ...
解决思路与方案
核心原因
默认的MockedProvider只会拦截没有指定client的Apollo请求,或者拦截与自身绑定的客户端实例的请求。你的业务代码指定了client: cmsClient,导致MockedProvider无法识别并拦截该请求。
具体方案
方案1:给MockedProvider传入指定客户端实例
将业务中使用的cmsClient传入MockedProvider的client属性,让MockedProvider关联该客户端,从而拦截它的请求:
const { getByText, getAllByText } = render( <MockedProvider mocks={mocks} addTypename={false} client={cmsClient} // 绑定特定客户端实例 > <ResultsScreen {...mockProps} /> </MockedProvider> );
注意:确保测试环境中的
cmsClient没有配置真实的请求端点,否则仍可能发送真实网络请求。可以在测试前重置客户端的链路为Mock链路。
方案2:通过依赖注入替换客户端
避免在业务代码中硬编码cmsClient,改为通过props或React上下文传入,测试时替换为专用的测试客户端:
- 调整业务代码:
// 通过props接收客户端实例 const ResultsScreen = ({ cmsClient, ...restProps }) => { const [ getSpecificReport, { loading: contentLoading, error: contentError, data: content }, ] = useLazyQuery<SpecificReportResponse>( SPECIFIC_REPORT(testResultsData?.getTestResults.testType), { client: cmsClient, fetchPolicy: "network-only", onCompleted: () => { setScreenData(); }, onError: (err) => { // 省略错误处理逻辑 }, } ); // 组件其他逻辑 };
- 测试中创建并传入测试客户端:
import { ApolloClient, InMemoryCache } from '@apollo/client'; import { MockLink } from '@apollo/client/testing'; // 创建测试用客户端,使用MockLink处理请求 const testCmsClient = new ApolloClient({ link: new MockLink(mocks), cache: new InMemoryCache(), }); const { getByText, getAllByText } = render( <ResultsScreen {...mockProps} cmsClient={testCmsClient} /> );
方案3:自定义请求匹配逻辑
如果无法修改客户端传入方式,可通过MockedProvider的matchRequest函数自定义匹配规则,确保能识别指定客户端的请求:
<MockedProvider mocks={mocks} addTypename={false} matchRequest={(request, mockRequest) => { // 匹配query、变量,可额外添加客户端相关判断 return ( request.query === mockRequest.query && JSON.stringify(request.variables) === JSON.stringify(mockRequest.variables) // 若需匹配特定客户端,可根据request上下文判断: // request.context.client === cmsClient ); }} > <ResultsScreen {...mockProps} /> </MockedProvider>
额外优化:修复测试中的waitFor写法
原测试中waitFor内的setTimeout是多余的,waitFor本身会循环等待断言通过,修改为:
await waitFor(() => { expect(getByText(/Something/)).toBeInTheDocument(); });
内容的提问来源于stack exchange,提问作者Cesar
相关产品推荐
相关产品推荐

