Mocking
The strongly-typed nature of a GraphQL API lends itself extremely well to mocking. This is an important part of a GraphQL-First development process, because it enables frontend developers to build out UI components and features without having to wait for a backend implementation.
Even with a backend that is already built, mocking allows you to test your UI without waiting on slow database requests or building out a component harness with a tool like React Storybook.
#
Default mock exampleLet's take a look at how we can mock a GraphQL schema with just one line of code, using the default mocking logic you get out of the box with graphql-tools
.
To start, let's grab the schema definition string from the makeExecutableSchema
example in the "Generating a schema" article.
Note: If your schema has custom scalar types, you still need to define the
__serialize
,__parseValue
, and__parseLiteral
functions, and pass them inside the second argument tomakeExecutableSchema
.
This mocking logic simply looks at your schema and makes sure to return a string where your schema has a string, a number for a number, etc. So you can already get the right shape of result. But if you want to use the mocks to do sophisticated testing, you will likely want to customize them to your particular data model.
#
Customizing mocksThis is where the mocks
option comes in, it's an object that describes your desired mocking logic. This is similar to the resolverMap
in makeExecutableSchema
, but has a few extra features aimed at mocking.
It allows you to specify functions that are called for specific types in the schema, for example:
You can also use this to describe object types, and the fields can be functions too:
In this example, we are using casual, a fake data generator for JavaScript, so that we can get a different result every time the field is called. You might want to use a collection of fake objects, or a generator that always uses a consistent seed, if you are planning to use the data for testing.
#
Mocking Custom Scalar TypesIn order to Mock Custom Scalar Types, you need to declare them in your Schema. Let's look at an example for declaring DateTime Custom Scalar in our Schema:
This will make DateTime Custom Scalar available to be used in the Schema.
The next step is to define a function that returns a value (fixed or random) for the Custom Scalar. Look at the following example, in which we're mocking a fixed value for the DateTime Custom Scalar Type:
Similarly, if you want to mock a random value for the Custom Scalar, you can use a library. We're using casual, as in the example above:
The final step is to use the mocks
object and schema
to mock the server.
Now, when you make a Query which response contains the DateTime Scalar Type, the DateTime function will return a value for it.
#
Using lists in mocksTo define a mock for a list, simply return an empty array of the desired length as mock value for the field:
#
Abstract typesIf you'd like to provide a mock for an Union
or Interface
type, you need to provide the type with an extra __typename
.
#
Applying mutationsUse resolvers
option of addMocksToSchema
to implement custom resolvers that interact with the MockStore
, especially to mutate field values.
As a result, any query that queries the field name
of the User
referenced in me
will get the updated value.
Note the sugar signature of set
:
*byId
fields#
Handling By default, *byId
(like userById(id: ID!)
) field will return an entity that does not have the same id
as the one queried. We can fix that:
Note that, by default, the id
or _id
field will be used as storage key and the store will make sure the storage key and the field value are equal. You can change the key field using the option typePolicies
.
#
Mocking a paginationThe idea is that the MockStore
contains the full list, as field value, and that the resolver queries the store and slice the result:
#
Relay-style paginationThe principles stay the same than for basic pagination:
#
Mocking a schema using introspectionThe GraphQL specification allows clients to introspect the schema with a special set of types and fields that every schema must include. The results of a standard introspection query can be used to generate an instance of GraphQLSchema which can be mocked as explained above.
This helps when you need to mock a schema defined in a language other than JS, for example Go, Ruby, or Python.
To convert an introspection query result to a GraphQLSchema
object, you can use the buildClientSchema
utility from the graphql
package.
#
API#
addMocksToSchemaGiven an instance of GraphQLSchema and a mock object, addMocksToSchema
returns a new schema that can return mock data for any valid query that is sent to the server. If mocks
is not passed, the defaults will be used for each of the scalar types. If preserveResolvers
is set to true
, existing resolvers will not be overwritten to provide mock data. This can be used to mock some parts of the server and not others.
#
mockServermockServer
is just a convenience wrapper on top of addMocksToSchema
. It adds your mock resolvers to your schema and returns a client that will correctly execute
your query with variables. Note: when executing queries from the returned server,
context
and root
will both equal {}
.
#
MockStoreThe MockStore
is holding the generated mocks and can be used to access, generate or alter mocked values.
You can access the MockStore
either as argument of resolvers
option of addMocksToSchema
:
or by using createMockStore
(and use the store in addMocksToSchema
):
The content is accessible and modifiable via the methods get
and set
of the MockStore
. These methods have several signatures (see their typing)
but here are some examples:
#
getGet a field value from the store for the given type, store key and field name. If the the value for this field is not set, a value will be generated according to field return type and mock functions:
If the field is the key field of this type, the store key itself is returned. By default, id
and _id
fields are considered as key fields but it can be customized with option typePolicies
.
If the field's output type is a ObjectType
(or list of ObjectType
), it will return a Ref
(or array of Ref
), ie a reference to an entity in the store.
Ref
can then be used, for example to get a field value given of a Ref
:
As a shortcut, you can traverse the graph quickly with an array of field names (nested get):
You can generate a entity reference Ref
with:
Root types (Query
, Mutation
), which necessarily have only one entity, will use the special store key ROOT
to reference this only entity:
#
setSet a field value in the store for the given type, store key and field name:
If the field's output type is a ObjectType
(or list of ObjectType
), you can set a Ref
(or array of Ref
):
You can use a Ref
to set field name:
Set multiple field values at once:
Set a field value via graph traversal (nested set):
#
Migration from V7 and below#
Breaking change: Mock functions signatureMock functions does not receive resolvers' source, arguments and context anymore. Use resolvers
option of addMocksToSchema
to define "true" resolvers that can intract with MockStore
.
Example:
Read more about mocking pagination here
Mock functions can't return a Promise
anymore: it has to be a plain value. You can also use resolvers to work around this:
#
Breaking change: preserved resolvers source argument and abstract types mockingWhen preserved, resolvers will not receive plain mock data anymore as source but rather a Ref
that can be used to query the store:
If you used __resolveType
resolver for mocking interfaces and unions, rather use __typename
directly in mocks. See Abstract types.
Example:
#
Deprecated: MockListMockList
is deprecated, use plain arrays instead. See Using lists in mocks.
Example: