测试 useSubscription apollo hooks with react

Har*_*oln 12 subscription apollo apollo-server apollo-client react-hooks

测试useSubscription钩子我发现有点困难,因为该方法在Apollo 文档中被省略/未记录(在撰写本文时)。据推测,应该使用<MockedProvider />from来模拟它@apollo/react-testing,就像该链接中给出的示例中的突变一样。

测试我正在工作的订阅的加载状态:

成分:

const GET_RUNNING_DATA_SUBSCRIPTION = gql`
  subscription OnLastPowerUpdate {
    onLastPowerUpdate {
      result1,
      result2,
    }
  }
`;

const Dashboard: React.FC<RouteComponentProps & Props> = props => {
  const userHasProduct = !!props.user.serialNumber;

  const [startGetRunningData] = useMutation(START_GET_RUNNING_DATA);

  const [stopGetRunningData] = useMutation(STOP_GET_RUNNING_DATA);

  useEffect(() => {
    startGetRunningData({
      variables: { serialNumber: props.user.serialNumber },
    });

    return () => {
      stopGetRunningData();
    };
  }, [startGetRunningData, stopGetRunningData, props]);

  const SubscriptionData = (): any => {
    const { data, loading } = useSubscription(GET_RUNNING_DATA_SUBSCRIPTION);

    if (loading) {
      return <Heading>Data loading...</Heading>;
    }

    const metrics = [];
    if (data) {
      console.log('DATA NEVER CALLED IN TEST!');
    }

    return metrics;
  };

  if (!userHasProduct) {
    return <Redirect to="/enter-serial" />;
  }

  return (
    <>
      <Header />
      <PageContainer size="midi">
        <Panel>
          <SubscriptionData />
        </Panel>
      </PageContainer>
    </>
  );
};
Run Code Online (Sandbox Code Playgroud)

并成功测试订阅的加载状态:

import React from 'react';
import thunk from 'redux-thunk';
import { createMemoryHistory } from 'history';
import { create } from 'react-test-renderer';
import { Router } from 'react-router-dom';
import wait from 'waait';
import { MockedProvider } from '@apollo/react-testing';
import { Provider } from 'react-redux';

import configureMockStore from 'redux-mock-store';

import Dashboard from './Dashboard';

import {
  START_GET_RUNNING_DATA,
  STOP_GET_RUNNING_DATA,
  GET_RUNNING_DATA_SUBSCRIPTION,
} from './queries';

const mockStore = configureMockStore([thunk]);

const serialNumber = 'AL3286wefnnsf';

describe('Dashboard page', () => {
  let store: any;

  const fakeHistory = createMemoryHistory();

  const mocks = [
    {
      request: {
        query: START_GET_RUNNING_DATA,
        variables: {
          serialNumber,
        },
      },
      result: {
        data: {
          startFetchingRunningData: {
            startedFetch: true,
          },
        },
      },
    },
    {
      request: {
        query: GET_RUNNING_DATA_SUBSCRIPTION,
      },
      result: {
        data: {
          onLastPowerUpdate: {
            result1: 'string',
            result2: 'string'
          },
        },
      },
    },
    {
      request: {
        query: STOP_GET_RUNNING_DATA,
      },
      result: {
        data: {
          startFetchingRunningData: {
            startedFetch: false,
          },
        },
      },
    },
  ];

  afterEach(() => {
    jest.resetAllMocks();
  });

  describe('when initialising', () => {
    beforeEach(() => {
      store = mockStore({
        user: {
          serialNumber,
          token: 'some.token.yeah',
          hydrated: true,
        },
      });
      store.dispatch = jest.fn();
    });

    it('should show a loading state', async () => {
      const component = create(
        <Provider store={store}>
          <MockedProvider mocks={mocks} addTypename={false}>
            <Router history={fakeHistory}>
              <Dashboard />
            </Router>
          </MockedProvider>
        </Provider>,
      );

      expect(component.root.findAllByType(Heading)[0].props.children).toBe(
        'Data loading...',
      );
    });
  });
});
Run Code Online (Sandbox Code Playgroud)

添加另一个测试以等待从传入的模拟中解析数据,根据测试 useMutation文档中最后一个示例的说明,您必须等待它。

破碎测试:

it('should run the data', async () => {
      const component = create(
        <Provider store={store}>
          <MockedProvider mocks={mocks} addTypename={false}>
            <Router history={fakeHistory}>
              <Dashboard />
            </Router>
          </MockedProvider>
        </Provider>,
      );
      await wait(0);
    });
Run Code Online (Sandbox Code Playgroud)

错误的测试抛出:

No more mocked responses for the query: subscription OnLastPowerUpdate {
Run Code Online (Sandbox Code Playgroud)

依赖项:

    "@apollo/react-common": "^3.1.3",
    "@apollo/react-hooks": "^3.1.3",
    "@apollo/react-testing": "^3.1.3",
Run Code Online (Sandbox Code Playgroud)

我已经尝试过的事情:

Github repo 示例:

https://github.com/harrylincoln/apollo-subs-testing-issue

有没有人可以帮忙?

Fra*_*ion 1

我在这里看到的问题是,您在组件SubscriptionData内部声明该组件Dashboard,因此下次Dashboard重新渲染该组件时,该SubscriptionData组件将被重新创建,您将看到错误消息:

查询不再有模拟响应:订阅 OnLastPowerUpdate

我建议您将SubscriptionData组件从Dashboard组件中取出,这样它只会创建一次

const SubscriptionData = (): any => {
  const { data, loading } = useSubscription(GET_RUNNING_DATA_SUBSCRIPTION);

  if (loading) {
    return <Heading>Data loading...</Heading>;
  }

  const metrics = [];
  if (data) {
    console.log('DATA NEVER CALLED IN TEST!');
  }

  return metrics;
};

const Dashboard: React.FC<RouteComponentProps & Props> = props => {
  const userHasProduct = !!props.user.serialNumber;

  const [startGetRunningData] = useMutation(START_GET_RUNNING_DATA);

  const [stopGetRunningData] = useMutation(STOP_GET_RUNNING_DATA);

  useEffect(() => {
    startGetRunningData({
      variables: { serialNumber: props.user.serialNumber },
    });

    return () => {
      stopGetRunningData();
    };
  }, [startGetRunningData, stopGetRunningData, props]);

  if (!userHasProduct) {
    return <Redirect to="/enter-serial" />;
  }

  return (
    <>
      <Header />
      <PageContainer size="midi">
        <Panel>
          <SubscriptionData />
        </Panel>
      </PageContainer>
    </>
  );
};
Run Code Online (Sandbox Code Playgroud)

对于测试,你可以尝试这样的事情:

let component;
it('should show a loading state', async () => {
      component = create(
        <Provider store={store}>
          <MockedProvider mocks={mocks} addTypename={false}>
            <Router history={fakeHistory}>
              <Dashboard />
            </Router>
          </MockedProvider>
        </Provider>,
      );
      expect(component.root.findAllByType(Heading)[0].props.children).toBe(
        'Data loading...',
      );

      await wait(0);
});

it('should run the data', async () => {
      expect(
        // another test here
        component.root...
      ).toBe();
});
Run Code Online (Sandbox Code Playgroud)