I have a simple Apollo set up. In this CodeSandbox example I'm using the MockedProvider
but the live one also works:
https://codesandbox.io/s/winter-glitter-o8ug5?file=/src/App.js:0-1164
import { ApolloClient, InMemoryCache } from "@apollo/client";
import { gql, useQuery, ApolloProvider } from "@apollo/client";
import { MockedProvider } from "@apollo/client/testing";
const client = new ApolloClient({
uri: "https://48p1r2roz4.sse.codesandbox.io",
cache: new InMemoryCache()
});
const EXCHANGE_RATES_1 = gql`
query GetExchangeRates {
rates(currency: "USD") {
rate
}
}
`;
const mocks = [
{
request: {
query: EXCHANGE_RATES_1
},
result: {
data: {
rates: [
{
__typename: "ExchangeRate",
rate: "123"
}
]
}
}
}
];
const MyComponent = () => {
const { data: data1, loading: loading1 } = useQuery(EXCHANGE_RATES_1);
if (loading1) return <p>Loading...</p>;
return (
<div>
<h1>{data1?.rates[0].rate}</h1>
</div>
);
};
const AppMocked = () => {
return (
<MockedProvider addTypename={false} mocks={mocks}>
<MyComponent />
</MockedProvider>
);
};
const AppLive = () => {
return (
<ApolloProvider client={client}>
<MyComponent />
</ApolloProvider>
);
};
export default AppMocked;
I need to have an additional useQuery
in MyComponent
. I know this is a weird example as it should just be one query but it illustrates the issue I'm running into.
https://codesandbox.io/s/magical-dewdney-j451d?file=/src/App.js:0-1630
import { ApolloClient, InMemoryCache } from "@apollo/client";
import { gql, useQuery, ApolloProvider } from "@apollo/client";
import { MockedProvider } from "@apollo/client/testing";
const client = new ApolloClient({
uri: "https://48p1r2roz4.sse.codesandbox.io",
cache: new InMemoryCache()
});
const EXCHANGE_RATES_1 = gql`
query GetExchangeRates {
rates(currency: "USD") {
rate
}
}
`;
const EXCHANGE_RATES_2 = gql`
query GetExchangeRates {
rates(currency: "USD") {
currency
}
}
`;
const mocks = [
{
request: {
query: EXCHANGE_RATES_1
},
result: {
data: {
rates: [
{
__typename: "ExchangeRate",
rate: "123"
}
]
}
}
},
{
request: {
query: EXCHANGE_RATES_2
},
result: {
data: {
rates: [
{
__typename: "ExchangeRate",
currency: "YOLO"
}
]
}
}
}
];
const MyComponent = () => {
const { data: data1, loading: loading1 } = useQuery(EXCHANGE_RATES_1);
const { data: data2, loading: loading2 } = useQuery(EXCHANGE_RATES_2);
if (loading1 || loading2) return <p>Loading...</p>;
return (
<div>
<h1>{data1?.rates[0].rate}</h1>
<h2>{data2?.rates[0].currency}</h2>
</div>
);
};
const AppMocked = () => {
return (
<MockedProvider addTypename={false} mocks={mocks}>
<MyComponent />
</MockedProvider>
);
};
const AppLive = () => {
return (
<ApolloProvider client={client}>
<MyComponent />
</ApolloProvider>
);
};
export default AppMocked;
The live version works fine but the mocked version has an empty H1. When I log out data1
I can see that it initially does have data but on subsequent renders becomes 'undefined'
I couldn't see anything in the docs to explain why the mocking isnt working: https://www.apollographql.com/docs/react/development-testing/testing/
For some reason, in your setup, the queries are called more than once.
This causes a "No more mocked responses for the query" error behind the scenes (see mockLink.ts) as each query call consumes one mocked response.
The first time the query is called, it returns the mocked data, which is why you initially see it. The next call though throws an error and sets the data to undefined
.
I found two ways to solve this:
Option 1 Disable caching on the MockedProvider by setting the fetch-policy
to no-cache
:
<MockedProvider
mocks={mocks}
defaultOptions={{
watchQuery: { fetchPolicy: 'no-cache' },
query: { fetchPolicy: 'no-cache' },
}}
>
<MyComponent />
</MockedProvider>
Option 2 Allow the mocked response to be used more than once by providing a newData
function:
const mocks = [
{
request: {
query: EXCHANGE_RATES_1
},
result: {
data: {
rates: [
{
__typename: "ExchangeRate",
rate: "123"
}
]
}
},
newData: () => ({
data: {
rates: [
{
__typename: "ExchangeRate",
rate: "123"
}
]
}
})
},
{
request: {
query: EXCHANGE_RATES_2
},
result: {
data: {
rates: [
{
__typename: "ExchangeRate",
currency: "YOLO"
}
]
}
},
newData: () => ({
data: {
rates: [
{
__typename: "ExchangeRate",
currency: "YOLO"
}
]
}
})
}
];
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With