When working with React components that use Apollo Client, you often need to mock the Apollo Provider for testing or Storybook. This allows you to test your components in isolation without setting up a full GraphQL server or making actual network requests.
The challenge is that Apollo's ApolloProvider expects a full Apollo Client instance, which can be complex to mock. However, for most testing scenarios, you don't need the full client—you just need to mock the query methods that your components use.
Here's a simple solution that creates a mock Apollo Provider that accepts predefined query results. This is perfect for:
- Storybook: Show your components with different data states without a backend
- Unit tests: Test component rendering and behavior with controlled data
- Integration tests: Verify component interactions without network calls
The MockApolloQuery component wraps your components and provides mock query results based on the query name. It handles both readQuery and watchQuery methods, which covers most common Apollo usage patterns.
import { ApolloProvider } from 'react-apollo'
import React from 'react'
function resultForQuery(
ctx: any,
results: { [queryName: string]: { data?: any; loading?: boolean } },
) {
const query = ctx.query.definitions.find(
(op: any) => op.kind === 'OperationDefinition' && Object.keys(results).includes(op.name.value),
)
if (query) {
return {
subscribe: () => ({
query: 'dummy-MockApolloQuery',
}),
resetQueryStoreErrors: () => {},
options: {
fetchPolicy: 'cache-only',
},
setOptions: async () => new Promise(resolve => setTimeout(resolve, 0)),
getCurrentResult: () => {
return {
data: results[query.name.value].data,
loading: results[query.name.value].loading ?? false,
}
},
}
}
throw new Error(
`Called with unknown query ${ctx.query.definitions
.filter((op: any) => op.kind === 'OperationDefinition')
.map((op: any) => op.name.value)}, must mock all queries for MockApolloQuery to work.`,
)
}
export function MockApolloQuery({
results,
children,
}: {
results: { [queryName: string]: { data?: any; loading?: boolean } }
children: React.ReactNode
}) {
return (
<ApolloProvider
client={
{
readQuery: (ctx: any) => resultForQuery(ctx, results),
watchQuery: (ctx: any) => resultForQuery(ctx, results),
} as any
}
>
{children}
</ApolloProvider>
)
}
It is the best.