You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何测试@apollo/client中OnSubscriptionData的功能?

Testing onSubscriptionData in Apollo Client Subscriptions

Hey there! I’ve been in your shoes before—testing onSubscriptionData in Apollo Client can feel tricky since examples are sparse. Let’s break this down into actionable steps with concrete code examples that you can adapt to your project.

Core Approach

The key idea is to use Apollo’s MockedProvider to simulate subscription events, trigger the onSubscriptionData callback, and then assert that your custom logic (like updating cache, state, or UI) behaves as expected. We’ll use Jest and React Testing Library here, but the concepts translate to other testing frameworks too.

First, Let’s Assume Your Component Code

Say you have a component that uses useSubscription with an onSubscriptionData handler like this:

import { useSubscription } from '@apollo/client';
import { NEW_MESSAGE_SUBSCRIPTION } from './graphql/subscriptions';
import { GET_MESSAGES_QUERY } from './graphql/queries';

export const ChatFeed = () => {
  const { data } = useSubscription(NEW_MESSAGE_SUBSCRIPTION, {
    onSubscriptionData: ({ subscriptionData, client }) => {
      // The logic we want to test: update local cache with new message
      const existingMessages = client.readQuery({ query: GET_MESSAGES_QUERY });
      client.writeQuery({
        query: GET_MESSAGES_QUERY,
        data: {
          messages: [...existingMessages.messages, subscriptionData.data.newMessage]
        }
      });
    }
  });

  return <div className="chat-feed">{/* Render messages */}</div>;
};

Step-by-Step Test Implementation

1. Set Up MockedProvider and Initial State

We’ll create a mock subscription response and initialize the Apollo cache with some starting data to simulate a real-world scenario.

2. Write the Test for Cache Updates

Here’s how to verify that onSubscriptionData correctly updates the cache:

import { render, waitFor } from '@testing-library/react';
import { MockedProvider, ApolloClient, InMemoryCache } from '@apollo/client';
import { ChatFeed } from './ChatFeed';
import { NEW_MESSAGE_SUBSCRIPTION, GET_MESSAGES_QUERY } from './graphql';

// Initial cache data to simulate existing messages
const initialCache = {
  messages: [{ id: '1', content: 'Hey!' }]
};

// Mock subscription response that triggers onSubscriptionData
const subscriptionMock = {
  request: {
    query: NEW_MESSAGE_SUBSCRIPTION,
    // Add variables here if your subscription uses them!
  },
  result: {
    data: {
      newMessage: { id: '2', content: 'Hi from subscription!' }
    }
  }
};

test('onSubscriptionData updates cache with new message', async () => {
  // Create a client with pre-populated cache
  const client = new ApolloClient({
    cache: new InMemoryCache().restore(initialCache),
    link: jest.fn() // Mock link since we're using MockedProvider
  });

  // Render component wrapped in MockedProvider
  render(
    <MockedProvider mocks={[subscriptionMock]} client={client}>
      <ChatFeed />
    </MockedProvider>
  );

  // Wait for the subscription callback to fire and update cache
  await waitFor(async () => {
    const cachedMessages = await client.readQuery({ query: GET_MESSAGES_QUERY });
    // Assert cache was updated correctly
    expect(cachedMessages.messages).toHaveLength(2);
    expect(cachedMessages.messages[1].content).toBe('Hi from subscription!');
  });
});

3. Testing UI/State Updates

If your onSubscriptionData updates component state (like showing a notification), adjust the test to check the UI:

// Updated component with notification state
export const ChatFeed = () => {
  const [notification, setNotification] = useState('');
  const { data } = useSubscription(NEW_MESSAGE_SUBSCRIPTION, {
    onSubscriptionData: ({ subscriptionData }) => {
      setNotification(`New message: ${subscriptionData.data.newMessage.content}`);
    }
  });

  return (
    <div>
      <div className="notification">{notification}</div>
      <div className="chat-feed">{/* Render messages */}</div>
    </div>
  );
};

// Corresponding test
test('onSubscriptionData displays notification', async () => {
  render(
    <MockedProvider mocks={[subscriptionMock]}>
      <ChatFeed />
    </MockedProvider>
  );

  await waitFor(() => {
    const notification = screen.getByText(/New message: Hi from subscription!/i);
    expect(notification).toBeInTheDocument();
  });
});

Testing Edge Cases

Don’t forget to test error scenarios! For example, if the subscription fails:

const errorSubscriptionMock = {
  request: {
    query: NEW_MESSAGE_SUBSCRIPTION,
  },
  error: new Error('Subscription connection failed')
};

test('onSubscriptionData handles subscription errors', async () => {
  render(
    <MockedProvider mocks={[errorSubscriptionMock]}>
      <ChatFeed />
    </MockedProvider>
  );

  await waitFor(() => {
    const errorText = screen.getByText(/Subscription connection failed/i);
    expect(errorText).toBeInTheDocument();
  });
});

Key Tips

  • Match Queries Exactly: Ensure your mock’s request matches the subscription query used in your component (including variables—if your subscription uses them, add them to the mock request!).
  • Wait for Async Actions: Always use waitFor or findBy* queries to wait for the onSubscriptionData callback to execute, since it’s triggered asynchronously.
  • Mock Dependencies: If your onSubscriptionData relies on external services or utilities, mock those to isolate the logic you’re testing.

内容的提问来源于stack exchange,提问作者Roei Rom

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 17:33:12