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

Posted

技术标签:

【中文标题】如何使用带有 react-apollo 的模拟数据进行测试【英文标题】:How to use a mocked data with react-apollo for tests 【发布时间】:2018-01-23 19:53:33 【问题描述】:

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

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

https://dev-blog.apollodata.com/mocking-your-server-with-just-one-line-of-code-692feda6e9cd http://dev.apollodata.com/tools/graphql-tools/mocking.html#addMockFunctionsToSchema

但实际上并没有一个示例说明如何在我的应用测试中使用这个模拟服务器以避免撞到服务器。

我的目标是设置集成测试以断言应用程序确实在工作:

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?

【问题讨论】:

【参考方案1】:

我写了一篇博文,可能会有所帮助:http://blog.dideric.is/2018/03/18/Testing-apollo-containers/

Apollo 有一个名为 LinkSchema 的东西,这使得 Carlos 提到的第一种方法更容易。它仍然需要一些设置,但我认为这是值得的。如果您手动创建响应,则您必须更加担心保持测试最新/当架构更改并且您没有在代码中考虑它时得到误报。

【讨论】:

【参考方案2】:

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

第一个是使用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

【讨论】:

感谢您的提问和回答!经过数小时的搜索,这让我走上了正确的道路! 很高兴能提供帮助,我也为此苦苦挣扎了一段时间 @CarlosMartinez 哪一个是公认的最佳实践或最受支持的库/选项? @CarlosMartinez 这太棒了!我知道如何测试服务器端代码没问题,但对如何使用react-apollo 集成管理前端代码库感到迷茫。写得很棒,非常感谢您提供关于这个问题的研究。 我在尝试运行此代码时收到错误 TypeError: Cannot read property 'apply' of undefined【参考方案3】:

你也可以使用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 组件。最好测试那些处于“未连接”状态的人。

【讨论】:

是的,你可以,我对 MockedProvider 的唯一问题是你不能将 addTypename: false 传递给底层的 apollo 客户端,所以你的模拟需要在任何地方都包含 __typename。他们合并了我打开的一个 PR 来解决这个问题 github.com/apollographql/react-apollo/pull/1001 哦,我明白了,那是你的道具!哈哈 - 我今天才注意到它 使用渲染道具而不是 HOC 时你会怎么做? 我不再使用这种方法,我转而对我的组件进行集成测试。另外,我还没有使用 render prop 组件,所以我不能告诉你,对不起。 @DanielOcampo 我没有时间写一篇文章,但我用我使用这种方法的项目中的一些代码 sn-ps 做了一个要点。 gist.github.com/devboell/971c5477532fcae674c49110ab720ceb

以上是关于如何使用带有 react-apollo 的模拟数据进行测试的主要内容,如果未能解决你的问题,请参考以下文章

使用带有反应生命周期方法的 react-apollo 2.1 突变

从 react-apollo 模拟 <Query />

react-apollo 突变组件将空字符串发送为 null

react-apollo 中的 data.refetch() 函数如何工作

如何在 react-apollo graphql 查询中正确使用动态变量?

如何在 react-apollo 中等待多个 useLazyQuery