2
votes

I'd like to be able to mock out some queries on the client-side so I don't have to provide a GraphQL endpoint in order to work on my React app.

According to the Apollo docs, it looks like I should be using apollo-link-schema. I've tried to follow the example, but in my case I end up with an error: Network error: Expected undefined to be a GraphQL schema. when trying to access the query result inside my wrapped component.

Can someone help me understand what I'm doing wrong here?

Here's a fully contained index.js example to illustrate my problem:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

import { ApolloProvider } from 'react-apollo';
import { ApolloClient } from 'apollo-client';
import { SchemaLink } from "apollo-link-schema";
import { InMemoryCache } from 'apollo-cache-inmemory';
import { ApolloCache } from 'apollo-cache';

import gql from 'graphql-tag';
import { graphql } from 'react-apollo';

import { makeExecutableSchema, addMockFunctionsToSchema, MockList } from 'graphql-tools';
const typeDefs = `
type Query {
  me: Person!
}

type Person {
    name: String!
}
`;
const schema = makeExecutableSchema({ typeDefs });
addMockFunctionsToSchema({ schema });

const schemaLink = new SchemaLink(schema);
const client = new ApolloClient({
    link: schemaLink,
    cache: new InMemoryCache()
});

class App extends Component {
  render() {
        if (this.props.query && this.props.query.loading) {
            return <div>Loading...</div>
        }

        if (this.props.query && this.props.query.error) {
            return <div>Error: {this.props.query.error.message}</div>
        }

        const person = this.props.query.me;
        return <div> {person.name} </div>
  }
}

const personQuery = gql`
    query PersonQuery {
        me {
            name
        }
    }
`;
App = graphql(personQuery, { name: 'query' })(App);

ReactDOM.render(
    <ApolloProvider client={client}>
        < App />
    </ApolloProvider>
    , document.getElementById('root')
);
2

2 Answers

0
votes

Update: I can get things working using MockedProvider from react-apollo/test-utils but I'm wondering if this is the best way forward. For example, as far as I can tell using MockedProvider requires that you set up exact matches for each query your component might perform, whereas addMockFunctionsToSchema from graphql-tools will let you customize your mocks more flexibly, as indicated in the docs -- I just wish I could make that work.

Here's the updated index.js file example working with MockedProvider:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

import { ApolloProvider } from 'react-apollo';
import { ApolloClient } from 'apollo-client';
import { SchemaLink } from "apollo-link-schema";
import { InMemoryCache } from 'apollo-cache-inmemory';
import { ApolloCache } from 'apollo-cache';

import gql from 'graphql-tag';
import { graphql } from 'react-apollo';

import { MockedProvider } from 'react-apollo/test-utils';

import { makeExecutableSchema, addMockFunctionsToSchema, MockList } from 'graphql-tools';
const typeDefs = `
type Query {
  me: Person!
}

type Person {
    name: String!
}
`;
const schema = makeExecutableSchema({ typeDefs });
addMockFunctionsToSchema({ schema });

const schemaLink = new SchemaLink(schema);
const client = new ApolloClient({
    link: schemaLink,
    cache: new InMemoryCache()
});

class App extends Component {
  render() {
        if (this.props.query && this.props.query.loading) {
            return <div>Loading...</div>
        }

        if (this.props.query && this.props.query.error) {
            return <div>Error: {this.props.query.error.message}</div>
        }

        const person = this.props.query.me;
        return <div> {person.name} </div>
  }
}

const personQuery = gql`
    query PersonQuery {
        me {
            __typename
            name
        }
    }
`;
const personQueryMockedData = {
    me: {
        __typename: 'String',
        name: 'Gabe'
    }
}


App = graphql(personQuery, { name: 'query' })(App);

ReactDOM.render(
    <MockedProvider mocks={
        [
            { 
                request: { query: personQuery, variables: { cache: false } },
                result: { data: personQueryMockedData }
            }
        ]
    }>
        < App />
    </MockedProvider>
    , document.getElementById('root')
);
0
votes

I think the link needs to be something else

Try this

import { ApolloLink, Observable } from 'apollo-link';

// ...
const link = new ApolloLink(operation => {
  const { query, operationName, variables } = operation;
  return new Observable(observer =>
    graphql(schema, print(query), null, null, variables, operationName)
      .then(result => {
        observer.next(result);
        observer.complete();
      })
      .catch(observer.error.bind(observer))
  );
});

In context of code

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

import { ApolloLink, Observable } from 'apollo-link';
import { ApolloProvider } from 'react-apollo';
import { ApolloClient } from 'apollo-client';
import { SchemaLink } from "apollo-link-schema";
import { InMemoryCache } from 'apollo-cache-inmemory';
import { ApolloCache } from 'apollo-cache';

import gql from 'graphql-tag';
import { graphql } from 'react-apollo';

import { makeExecutableSchema, addMockFunctionsToSchema, MockList } from 'graphql-tools';
const typeDefs = `
type Query {
  me: Person!
}

type Person {
    name: String!
}
`;
const schema = makeExecutableSchema({ typeDefs });
addMockFunctionsToSchema({ schema });

const link = new ApolloLink(operation => {
  const { query, operationName, variables } = operation;
  return new Observable(observer =>
    graphql(schema, print(query), null, null, variables, operationName)
      .then(result => {
        observer.next(result);
        observer.complete();
      })
      .catch(observer.error.bind(observer))
  );
});
const client = new ApolloClient({
  link,
  cache: new InMemoryCache()
});

class App extends Component {
  render() {
    if (this.props.query && this.props.query.loading) {
      return <div>Loading...</div>
    }

    if (this.props.query && this.props.query.error) {
      return <div>Error: {this.props.query.error.message}</div>
    }

    const person = this.props.query.me;
    return <div> {person.name} </div>
  }
}

const personQuery = gql`
    query PersonQuery {
        me {
            name
        }
    }
`;
console.log(personQuery)
App = graphql(personQuery, { name: 'query' })(App);

ReactDOM.render(
  <ApolloProvider client={client}>
    < App />
  </ApolloProvider>
  , document.getElementById('root')
);