react-apollo
react-apollo copied to clipboard
Can a subscription be mocked with MockedProvider (useSubscription hook)
I have a standard CRA app that uses @apollo/react-hooks. I'm using MockedProvider to write my tests but I'm not sure how to mock a subscription. I'm attempting to use this mock
{
request: {
query: gql`
subscription {
orderComplete
}
`,
},
},
But it results in this error:
Invariant Violation: Expecting a parsed GraphQL document. Perhaps you need to wrap the query string in a "gql" tag? (After some Googling, it seems this error is thrown in the event of a malformed request object making me think subscription is not an allowed option.)
Changing the mock to this
{
request: {
query: gql`
query {
orderComplete
}
`,
},
},
Results in
No more mocked responses for the query: subscription {
orderComplete {
id
}
}
, variables: {}
Is there a way to do this?
➕ 1️⃣
Same problem here.
Has anyone found a solution to this?
I was also running into some issues regarding this issue. I searched for more related issues and found this one: #3317.
Apparently, what's might be happening is that the subscriptions are not unsubscribing in the test suites. Try unsubscribing them in your component. Mine looks like this based on the howtographql hackernews clone example:
// Libraries
import React from 'react';
import { useQuery } from '@apollo/react-hooks';
// Dependencies
import queries from 'graphql/queries';
import updateCacheAfterVote from 'helpers/updateCacheAfterVote';
// Components
import Link from 'components/Link';
/**
* @function subscribeToNewLinks
* @param {function} subscribeToMore - Apollo subscribeToMore function from useQuery.
* @return {function} unsubscribe - Apollo unsubscribe function.
*/
async function subscribeToNewLinks(subscribeToMore) {
const unsubscribe = subscribeToMore({
document: queries.NEW_LINKS_SUBSCRIPTION,
updateQuery: (prev, { subscriptionData }) => {
if (!subscriptionData.data) return prev;
const { newLink } = subscriptionData.data;
const exists = prev.feed.links.find(({ id }) => id === newLink.id);
if (exists) return prev;
const newLinks = [newLink, ...prev.feed.links];
return Object.assign({}, prev, {
feed: {
links: newLinks,
count: newLinks.length,
__typename: prev.feed.__typename,
}
});
}
});
return unsubscribe;
}
export default function LinkList() {
const { loading, error, data, subscribeToMore } = useQuery(queries.FEED_QUERY);
React.useEffect(() => {
const unsubscribe = subscribeToNewLinks(subscribeToMore);
return () => { typeof unsubscribe === 'function' && unsubscribe() }
}, [subscribeToMore]);
if (loading) return (
<div data-test="component-link-list">
<div data-test="loading">Loading...</div>
</div>
);
if (error) return (
<div data-test="component-link-list">
<div data-test="error">Error!</div>
</div>
);
const linksToRender = data.feed.links;
return (
<div data-test="component-link-list">
{linksToRender.map((link, index) => (
<Link
data-test="link"
key={link.id}
link={link}
index={index}
updateStoreAfterVote={updateCacheAfterVote}
/>
))}
</div>
);
}
I am currently migrating to @apollo/react-hooks and it seems I can't test the side effects of a useSubscription in any way. I can't work out how to mock it.
A subscription is an event stream where each message can start a side effect.
We need the ability to mock this event stream so that we can test the side effects.
Ideally MockedProvider or otherwise gives us an emitMessage function we can call within tests that causes new data from useSubscription. This will let us trigger a sequence of messages for asserting the appropriate side effects in sequence
➕ 1️⃣
Same problem here. I can't find how to mock useSubscription.
+1
+1
MockProvider works fine for me with a subscription query. Here is my code:
"@apollo/react-hooks": "^3.1.3",
- Define gql queries
import gql from 'graphql-tag';
const TICKET_ADDED_SUBSCRIPTION = gql`
subscription ticketAdded($salon: String!) {
ticketAdded(salon: $salon) {
id
}
}
`;
const TICKET_UPDATED_SUBSCRIPTION = gql`
subscription ticketUpdated($salon: String!) {
ticketUpdated(salon: $salon) {
id
}
`;
- Mock data
const salon = { id: 'salon_dummy_id'};
const customerTicketListMock = [ { id: 'ticket_dummy_id' } ]
const queries = [
{
request: {
query: TICKET_ADDED_SUBSCRIPTION,
variables: {
salon: salon.id,
},
},
result: {
data: {
ticketAdded: customerTicketListMock[0],
},
},
},
{
request: {
query: TICKET_UPDATED_SUBSCRIPTION,
variables: {
salon: salon.id,
},
},
result: {
data: {
ticketAdded: customerTicketListMock[0],
},
},
}
];
.................
const client = new ApolloClient({
link: from([]),
cache: new InMemoryCache(),
});
const MockApolloProvider = ({ children }: { children: any }) => (
<ApolloProvider client={client}>
<MockedProvider mocks={queries} addTypename={false}>
{children}
</MockedProvider>
</ApolloProvider>
);
- Unit test
const props = {};
const ComponentTest = (
<MockApolloProvider>
<Component {...props} />
</MockApolloProvider>
);
@ttruongatl interesting. At the time that I opened this issue, I was using "@apollo/react-hooks": "^3.0.0", so perhaps this later version fixes the issue.
@joshnabbott Probably. Happy coding.
I can't work out how to mock updates to a single subscription. Only the first subscription response I list ever gets sent. If I list two query responses in the mock array, then the second listed response gets sent as a response to the second query. I was hoping that by listing two subscription responses, the second response would get sent (to the same query) after the first (according to delay).