Как использовать фиктивные данные с React-Apollo для тестов

Я использую response-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 вместо фактического API?


person Carlos Martinez    schedule 15.08.2017    source источник


Ответы (3)


Я нашел 2 разных способа создания фиктивных данных для запросов apollo-client:

Первый - использовать graphql-tools для создания имитационного сервера на основе вашего бэкэнда. schema, чтобы подключить этот фиктивный сервер к вашим тестам, можно создать mockNetworkInterface следующим образом:

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 была в актуальном состоянии в вашем клиенте, поэтому я счел это немного затруднительным.

Другой способ сделать это - использовать mockNetworkInterface, предоставленный apollo-client/test-utils

Вы можете использовать это так:

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

person Carlos Martinez    schedule 17.08.2017
comment
Спасибо за вопрос и ответ! Это поставило меня на правильный путь после нескольких часов поисков! - person Deividas Karzinauskas; 23.08.2017
comment
Рад помочь, я тоже некоторое время боролся с этим - person Carlos Martinez; 23.08.2017
comment
@CarlosMartinez, какая из них является общепринятой передовой практикой или лучшая поддерживаемая библиотека / вариант? - person arcom; 08.09.2017
comment
Наиболее поддерживаемым вариантом будет использование инструментов graphql, но для тестирования им нужна более простая интеграция. Я обнаружил, что использовать mockNetworkInterface намного проще. - person Carlos Martinez; 09.09.2017
comment
@CarlosMartinez, это было здорово! Я знаю, как протестировать код на стороне сервера без проблем, но я не знал, как управлять базой кода переднего плана с интеграцией react-apollo. Фантастическая статья, и большое спасибо за ваше исследование по этому поводу. - person rockchalkwushock; 11.09.2017
comment
Я получаю сообщение об ошибке TypeError: Cannot read property 'apply' of undefined при попытке запустить этот код. - person BrightIntelDusk; 29.06.2018

Вы также можете использовать 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>,
  )
})

Примечание. Используя фиктивный компонент, вы просто тестируете свои graphql() запросы и мутации, а также то, как вы их настроили (параметры, свойства, пропуск, переменные и т. Д.). Таким образом, вы не монтируете свои фактические компоненты React. Лучше протестировать их в «неподключенном» состоянии.

person devboell    schedule 14.09.2017
comment
Да, вы можете, моя единственная проблема с MockedProvider заключалась в том, что вы не могли передать addTypename: false базовому клиенту apollo, поэтому ваши макеты должны будут включать __typename везде. Они объединили PR, который я открыл, чтобы исправить это github.com/apollographql/react-apollo/pull / 1001 - person Carlos Martinez; 14.09.2017
comment
О, понятно, это твоя опора! ха-ха - я заметил это только сегодня - person devboell; 14.09.2017
comment
Как бы вы это сделали, используя Render Props вместо HOC? - person Robin Wieruch; 04.06.2018
comment
Я больше не использую этот подход, я перешел на интеграционное тестирование своих компонентов. Кроме того, я еще не использую компоненты рендеринга, поэтому я не могу вам сказать, извините. - person devboell; 05.06.2018
comment
@devboell, как у вас стратегия интеграционного тестирования? Не могли бы вы поделиться обзором того, как вы тестируете его таким образом, поскольку я считаю, что это может быть тот путь, по которому я тоже хочу идти. Благодарность - person Daniel Ocampo; 11.08.2018
comment
@DanielOcampo У меня нет времени писать об этом, но я изложил суть с помощью некоторых фрагментов кода из проекта, в котором я использую этот подход. gist.github.com/devboell/971c5477532fcae674c49110ab720ceb - person devboell; 12.08.2018

Некоторое время я написал сообщение в блоге, которое могло бы быть полезным: http://blog.dideric.is/2018/03/18/Testing-apollo-containers/

У Apollo есть нечто, называемое LinkSchema, которое делает Первый подход, о котором упоминал Карлос, намного проще. Это все еще требует некоторой настройки, но я думаю, что оно того стоит. Если вы создаете ответы вручную, вам нужно гораздо больше беспокоиться о поддержании ваших тестов в актуальном состоянии / получении ложных срабатываний, когда схема изменяется, и вы не учли это в своем коде.

person Loktopus    schedule 13.07.2018