react-apollo icon indicating copy to clipboard operation
react-apollo copied to clipboard

Can a subscription be mocked with MockedProvider (useSubscription hook)

Open joshnabbott opened this issue 6 years ago • 13 comments

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?

joshnabbott avatar Sep 23 '19 13:09 joshnabbott

➕ 1️⃣

nathanbirrell avatar Oct 08 '19 00:10 nathanbirrell

Same problem here.

caxone avatar Oct 16 '19 18:10 caxone

Has anyone found a solution to this?

JimmyMultani avatar Oct 25 '19 15:10 JimmyMultani

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>
  );
}

rmolinamir avatar Nov 11 '19 01:11 rmolinamir

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

mattburman avatar Nov 18 '19 17:11 mattburman

➕ 1️⃣

samfenwick avatar Nov 19 '19 11:11 samfenwick

Same problem here. I can't find how to mock useSubscription.

madispuk avatar Nov 19 '19 12:11 madispuk

+1

perevezencev avatar Dec 18 '19 12:12 perevezencev

+1

dimkadenisov avatar Dec 18 '19 12:12 dimkadenisov

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 avatar Dec 30 '19 18:12 ttruongatl

@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 avatar Dec 31 '19 17:12 joshnabbott

@joshnabbott Probably. Happy coding.

ttruongatl avatar Dec 31 '19 17:12 ttruongatl

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).

insidewhy avatar Apr 06 '20 03:04 insidewhy