From efc5302930eed13f57ca653592fe4af552dc9b5e Mon Sep 17 00:00:00 2001 From: Evans Hauser Date: Thu, 16 Aug 2018 11:21:13 -0700 Subject: [PATCH] Add option to mock the entire schema(i.e. set preserveResolvers) (#1546) * add mockEntireSchema as configuration for preserveResolvers * add mockEntireSchema to api reference * add changelog --- CHANGELOG.md | 2 + docs/source/api/apollo-server.md | 4 + docs/source/features/mocking.md | 38 +++++++++ .../apollo-server-core/src/ApolloServer.ts | 11 ++- packages/apollo-server-core/src/types.ts | 1 + .../src/ApolloServer.ts | 79 +++++++++++++++++++ .../src/index.ts | 4 +- 7 files changed, 134 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ca79cb66da..3b76a881b8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ All of the packages in the `apollo-server` repo are released with the same versi ### vNEXT +- Add option to mock the entire schema(i.e. sets preserveResolvers) [PR #1546](https://github.com/apollographql/apollo-server/pull/1546) + ### v2.0.2 - Release with Lerna 3 diff --git a/docs/source/api/apollo-server.md b/docs/source/api/apollo-server.md index cf873e1258b..36fdc322e0c 100644 --- a/docs/source/api/apollo-server.md +++ b/docs/source/api/apollo-server.md @@ -57,6 +57,10 @@ new ApolloServer({ A boolean enabling the default mocks or object that contains definitions +* `mockEntireSchema`: <`Boolean`> + + A boolean controlling whether existing resolvers are overridden by mocks. Defaults to true, meaning that all resolvers receive mocked values. + * `schemaDirectives`: <`Object`> Contains definition of schema directives used in the `typeDefs` diff --git a/docs/source/features/mocking.md b/docs/source/features/mocking.md index 27775df86cd..ceb6b01ebcd 100644 --- a/docs/source/features/mocking.md +++ b/docs/source/features/mocking.md @@ -143,6 +143,44 @@ const mocks = { For some more background and flavor on this approach, read the ["Mocking your server with one line of code"](https://medium.com/apollo-stack/mocking-your-server-with-just-one-line-of-code-692feda6e9cd) article on the Apollo blog. +### Using existing resolvers with mocks + +The default behavior for mocks is to overwrite the resolvers already present in the schema. To keep the existing resolvers, set the `mockEntireSchema` field to false. + +```js line=26 +const { ApolloServer } = require('apollo-server'); + +const typeDefs = gql` +type Query { + hello: String + resolved: String +} +`; + +const resolvers = { + Query: { + resolved: () => 'Resolved', + }, +}; + +const mocks = { + Int: () => 6, + Float: () => 22.1, + String: () => 'Hello', +}; + +const server = new ApolloServer({ + typeDefs, + resolvers, + mocks, + mockEntireSchema: false, +}); + +server.listen().then(({ url }) => { + console.log(`🚀 Server ready at ${url}`) +}); +``` + ## Mocking a schema using introspection The GraphQL specification allows clients to introspect the schema with a [special set of types and fields](https://facebook.github.io/graphql/#sec-Introspection) that every schema must include. The results of a [standard introspection query](https://github.com/graphql/graphql-js/blob/master/src/utilities/introspectionQuery.js) can be used to generate an instance of GraphQLSchema which can be mocked as explained above. diff --git a/packages/apollo-server-core/src/ApolloServer.ts b/packages/apollo-server-core/src/ApolloServer.ts index bb4f891d2f8..cc9327831f2 100644 --- a/packages/apollo-server-core/src/ApolloServer.ts +++ b/packages/apollo-server-core/src/ApolloServer.ts @@ -86,6 +86,7 @@ export class ApolloServerBase { typeDefs, introspection, mocks, + mockEntireSchema, extensions, engine, subscriptions, @@ -178,11 +179,15 @@ export class ApolloServerBase { }); } - if (mocks) { + if (mocks || typeof mockEntireSchema !== 'undefined') { addMockFunctionsToSchema({ schema: this.schema, - preserveResolvers: true, - mocks: typeof mocks === 'boolean' ? {} : mocks, + mocks: + typeof mocks === 'boolean' || typeof mocks === 'undefined' + ? {} + : mocks, + preserveResolvers: + typeof mockEntireSchema === 'undefined' ? false : !mockEntireSchema, }); } diff --git a/packages/apollo-server-core/src/types.ts b/packages/apollo-server-core/src/types.ts index 0525c5cf815..2ad80994c33 100644 --- a/packages/apollo-server-core/src/types.ts +++ b/packages/apollo-server-core/src/types.ts @@ -56,6 +56,7 @@ export interface Config context?: Context | ContextFunction; introspection?: boolean; mocks?: boolean | IMocks; + mockEntireSchema?: boolean; engine?: boolean | EngineReportingOptions; extensions?: Array<() => GraphQLExtension>; persistedQueries?: PersistedQueryOptions | false; diff --git a/packages/apollo-server-integration-testsuite/src/ApolloServer.ts b/packages/apollo-server-integration-testsuite/src/ApolloServer.ts index 0ee741af638..22981425598 100644 --- a/packages/apollo-server-integration-testsuite/src/ApolloServer.ts +++ b/packages/apollo-server-integration-testsuite/src/ApolloServer.ts @@ -271,6 +271,7 @@ export function testApolloServer( expect(result.data).toEqual({ testString: 'test string' }); expect(result.errors).toBeUndefined(); }); + it('allows mocks as boolean', async () => { const typeDefs = gql` type Query { @@ -305,6 +306,84 @@ export function testApolloServer( expect(result.data).toEqual({ hello: 'mock city' }); expect(result.errors).toBeUndefined(); }); + + it('allows mocks as an object without overriding the existing resolvers', async () => { + const typeDefs = gql` + type User { + first: String + last: String + } + type Query { + user: User + } + `; + const resolvers = { + Query: { + user: () => ({ + first: 'James', + last: 'Heinlen', + }), + }, + }; + const { url: uri } = await createApolloServer({ + typeDefs, + resolvers, + mocks: { + User: () => ({ + last: () => 'mock city', + }), + }, + }); + + const apolloFetch = createApolloFetch({ uri }); + const result = await apolloFetch({ + query: '{user{first last}}', + }); + expect(result.data).toEqual({ + user: { first: 'Hello World', last: 'mock city' }, + }); + expect(result.errors).toBeUndefined(); + }); + + // Need to fix bug in graphql-tools to enable mocks to override the existing resolvers + it.skip('allows mocks as an object with overriding the existing resolvers', async () => { + const typeDefs = gql` + type User { + first: String + last: String + } + type Query { + user: User + } + `; + const resolvers = { + Query: { + user: () => ({ + first: 'James', + last: 'Heinlen', + }), + }, + }; + const { url: uri } = await createApolloServer({ + typeDefs, + resolvers, + mocks: { + User: () => ({ + last: () => 'mock city', + }), + }, + mockEntireSchema: false, + }); + + const apolloFetch = createApolloFetch({ uri }); + const result = await apolloFetch({ + query: '{user{first last}}', + }); + expect(result.data).toEqual({ + user: { first: 'James', last: 'mock city' }, + }); + expect(result.errors).toBeUndefined(); + }); }); }); diff --git a/packages/apollo-server-integration-testsuite/src/index.ts b/packages/apollo-server-integration-testsuite/src/index.ts index cfaf9118bd5..36d7325780b 100644 --- a/packages/apollo-server-integration-testsuite/src/index.ts +++ b/packages/apollo-server-integration-testsuite/src/index.ts @@ -442,7 +442,7 @@ export default (createApp: CreateAppFunc, destroyApp?: DestroyAppFunc) => { }, }); return req.then(res => { - expect(res.status).toEqual(200); + expect(res.statusCode).toEqual(200); expect(res.body.errors).toBeDefined(); expect(res.body.errors.length).toEqual(1); expect(res.body.errors[0].message).toEqual( @@ -465,7 +465,7 @@ export default (createApp: CreateAppFunc, destroyApp?: DestroyAppFunc) => { }), }); return req.then(res => { - expect(res.status).toEqual(200); + expect(res.statusCode).toEqual(200); expect(res.body.errors).toBeDefined(); expect(res.body.errors.length).toEqual(1); expect(res.body.errors[0].message).toEqual('PersistedQueryNotFound');