web-dev-qa-db-ja.com

テストにreact-apolloでモックデータを使用する方法

私は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
});

実際のAPIの代わりに、ここでgraphql-toolsを使用してモックサーバーを使用するにはどうすればよいですか?

10
Carlos Martinez

Apollo-clientクエリのモックデータを作成する2つの異なる方法を見つけました。

1つ目は、 graphql-tools を使用して、バックエンドスキーマに基づいてモックサーバーを作成することです。このモックサーバーをテストに接続するために、次のような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スキーマを最新の状態にする必要があるため、少し面倒だと思いました。

これを行う別の方法は、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: falseApolloClientインスタンスに渡すことが重要です。そうしないと、すべてのクエリに__typenameを手動で追加する必要があります。

ここでmockNetworkInterfaceの実装を調べることができます: https://github.com/apollographql/apollo-test-utils/blob/master/src/mocks/mockNetworkInterface.ts

13
Carlos Martinez

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コンポーネント。 「接続されていない」状態でテストすることをお勧めします。

7
devboell

しばらくの間、役立つかもしれないブログ投稿を書きました: http://blog.dideric.is/2018/03/18/Testing-apollo-containers/

Apolloには LinkSchema と呼ばれるものがあり、Carlosが最初に言及したアプローチをはるかに簡単にします。まだセットアップは必要ですが、それだけの価値があると思います。手動で応答を作成している場合は、テストを最新の状態に保つことや、スキーマが変更されてコードでそれを考慮していないときに誤検知が発生することについて、さらに心配する必要があります。

1
Loktopus