【问题标题】:Testing useSubscription apollo hooks with react使用 React 测试 useSubscription apollo hooks
【发布时间】:2020-08-13 16:49:18
【问题描述】:

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

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

组件:

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

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

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...',
      );
    });
  });
});

添加另一个测试以等待从传入的模拟中解析数据,根据最后一个示例from the docs 中的说明测试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);
    });

错误的测试抛出:

No more mocked responses for the query: subscription OnLastPowerUpdate {

依赖关系:

    "@apollo/react-common": "^3.1.3",
    "@apollo/react-hooks": "^3.1.3",
    "@apollo/react-testing": "^3.1.3",

我已经尝试过的事情:

Github repo 示例:

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

有人可以帮忙吗?

【问题讨论】:

  • 可以添加GET_RUNNING_DATA_SUBSCRIPTION订阅的代码吗?
  • @Fraction 完成!谢谢!
  • 这没有多大帮助,你能分享一个带有示例代码的 repo 来重现你的问题吗?

标签: react-hooks apollo subscription apollo-client apollo-server


【解决方案1】:

我在这里看到的问题是,您在 Dashboard 组件内声明了 SubscriptionData 组件,因此下次重新渲染 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>
    </>
  );
};

对于测试,您可以尝试以下方法:

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

【讨论】:

  • 已经完成了,@Fraction。将&lt;SubscriptionData /&gt; 移到它自己的组件中,一切都不同了——我的眼睛好累!再次感谢
猜你喜欢
  • 2019-09-14
  • 2020-03-24
  • 2020-01-15
  • 2020-03-04
  • 2019-12-19
  • 2020-01-25
  • 1970-01-01
  • 2019-11-19
  • 2020-10-30
相关资源
最近更新 更多