如何使用带有 react-apollo 的模拟数据进行测试

How to use a mocked data with react-apollo for tests

我正在使用 react-apollo 构建一个使用 GraphQL 的客户端 API,但是,我非常专注于测试。我想要的是模拟服务器,这样我就可以轻松地测试应用程序而无需进行网络调用。

我找到了一些关于如何模拟服务器的指示:

但是在我的应用程序测试中并没有真正的示例说明如何使用这个模拟服务器来避免攻击服务器。

我的目标是设置集成测试以断言该应用确实在运行:

describe('Profile feature', () => {
  beforeAll(() => {
    store = setupStore();
    app = mount(
      <ApolloProvider store={store} client={apolloClient}>
        <ConnectedRouter history={history}>
          <App />
        </ConnectedRouter>
      </ApolloProvider>
    );
  });
});

商店使用的是 Redux,客户端是这样创建的:

const networkInterface = createNetworkInterface({
  uri: process.env.REACT_APP_API_URL
});

export const apolloClient = new ApolloClient({
  networkInterface
});

如何在此处使用带有 graphql-tools 的模拟服务器而不是实际的 API?

我发现了 2 种为 apollo-client 查询创建模拟数据的不同方法:

第一种是使用 graphql-tools 根据您的后端模式创建一个模拟服务器,为了将这个模拟服务器与您的测试连接起来,可以像这样创建一个模拟网络接口:

const { mockServer } = require("graphql-tools");
const { print } = require("graphql/language/printer");


class MockNetworkInterface {
  constructor(schema, mocks = {}) {
    if (schema === undefined) {
      throw new Error('Cannot create Mock Api without specifying a schema');
    }
    this.mockServer = mockServer(schema, mocks);
  }

  query(request) {
    return this.mockServer.query(print(request.query), request.variables);
  }
}

您可以将此网络接口传递给 ApolloClient 组件,它应该可以正常工作!

进行此设置需要您的客户端中的 API 架构是最新的,所以我发现这样做有点麻烦。

另一种方法是使用 apollo-client/test-utils

提供的 mockNetworkInterface

你可以这样使用:

import App from './App';
import { UserMock, PublicationMock } from '../__mocks__/data';
import { mockNetworkInterface } from 'react-apollo/test-utils';
import ApolloClient from 'apollo-client';
import { ApolloProvider } from 'react-apollo';

// We will be using here the exact same Query defined in our components
// We will provide a custom result or a custom error
const GraphQLMocks = [
  {
    request: {
      query: UserProfileQuery,
      variables: {}
    },
    result: {
      data: {
        current_user: UserMock
      }
    }
  }
];

// To set it up we pass the mocks to the mockNetworkInterface
const setupTests = () => {
  const networkInterface = mockNetworkInterface.apply(null, GraphQLMocks);
  const client = new ApolloClient({ networkInterface, addTypename: false });

  const wrapper = mount(
    <ApolloProvider client={client}>
      <App />
    </ApolloProvider>
  );

  return {
    store,
    wrapper
  };
};

// Then the tests look like this
describe('Profile feature', () => {
  test('Profile view should render User details', async () => {
    const { wrapper, store } = setupTests();

    const waitFor = createWaitForElement('.profile');

    await waitFor(wrapper);

    const tag = wrapper.find('.profile-username');
    expect(tag.text()).toEqual(`${UserMock.first_name} ${UserMock.last_name}`);
  });
});

addTypename: false 传递给 ApolloClient 实例很重要,否则您需要手动将 __typename 添加到所有查询。

您可以在此处检查 mockNetworkInterface 的实现:https://github.com/apollographql/apollo-test-utils/blob/master/src/mocks/mockNetworkInterface.ts

你也可以使用MockedProvider,这样就更简单了。

withPersons.js

import { gql, graphql } from 'react-apollo'

export const PERSONS_QUERY = gql`
  query personsQuery {
    persons {
      name
      city
    }
  }
`

export const withPersons = graphql(PERSONS_QUERY)

withPersons.test.js

/* eslint-disable react/prop-types */

import React, { Component } from 'react'
import { MockedProvider } from 'react-apollo/test-utils'

import { withPersons, PERSONS_QUERY } from '../withPersons'

it('withPersons', (done) => {
  const mockedData = {
    persons: [
      {
        name: 'John',
        city: 'Liverpool',
      },
      {
        name: 'Frank',
        city: 'San Diego',
      },
    ],
  }

  const variables = { cache: false }

  class Dummy extends Component {
    componentDidMount() {
      const { loading, persons } = this.props.data
      expect(loading).toBe(true)
      expect(persons).toBe(undefined)
    }

    componentWillReceiveProps(nextProps) {
      const { loading, persons } = nextProps.data

      expect(loading).toBe(false)
      expect(persons).toEqual(mockedData.persons)
      done()
    }

    render() {
      return null
    }
  }
  const DummyWithPersons = withPersons(Dummy)
  mount(
    <MockedProvider
      removeTypename
      mocks={[
        {
          request: { query: PERSONS_QUERY, variables },
          result: { data: mockedData } },
      ]}
    >
      <DummyWithPersons />
    </MockedProvider>,
  )
})

注意:通过使用 Dummy 组件,您只需测试 graphql() 查询和更改以及您配置它们的方式(选项、道具、跳过、变量等),因此您无需安装您的实际的 React 组件。最好测试处于 'unconnected' 状态的那些。

我写了一篇博客 post 一段时间可能会有帮助:http://blog.dideric.is/2018/03/18/Testing-apollo-containers/

Apollo 有一个叫做 LinkSchema 的东西,它使 Carlos 提到的第一种方法更容易。它仍然需要一些设置,但我认为这是值得的。如果您手动创建响应,则当模式发生变化并且您没有在代码中考虑它时,您必须更加担心让测试达到 date/getting 误报。