pnpm add mercurius-integration-testing
# or
yarn add mercurius-integration-testing
# or
npm install mercurius-integration-testing
- query, mutation & subscription support.
- DocumentNode and string support
- TypeScript support
- Support for graphql-typed-document-node, therefore, support for GraphQL Code Generator
- batchQueries support.
- headers management.
- cookies management.
// app.ts | app.js
import Fastify from 'fastify'
import Mercurius from 'mercurius'
import schema from './schema'
import { buildContext } from './buildContext'
export const app = Fastify()
app.register(Mercurius, {
schema,
resolvers: {},
context: buildContext,
allowBatchedQueries: true,
})
// integration.test.js | integration.test.ts
import { createMercuriusTestClient } from 'mercurius-integration-testing'
import { app } from '../app'
// ...
const testClient = createMercuriusTestClient(app)
expect(testClient.query('query { helloWorld }')).resolves.toEqual({
data: {
helloWorld: 'helloWorld',
},
})
Create a testing client instance, you should give it the fastify instance in which Mercurius was already registered, and optionally, some options
const client = createMercuriusTestClient(app, {
/**
* Optional, specify headers to be added to every request in the client
*/
headers: {
authorization: 'hello-world',
},
/**
* Optional, by default it points to /graphql
*/
url: '/graphql',
/**
* Optional, specify cookies to be added to every request in the client
*/
cookies: {
authorization: 'hello-world',
},
})
.query
and.mutate
are basically the same function, but for readability, both exists
// You can give it a simple string
const queryResponse = await client.query(`
query {
helloWorld
}
`)
// Data returned from the API
queryResponse.data
// Possible array of errors from the API
queryResponse.errors
// You can also call `mutate`
// to improve readability for mutations
const mutationResponse = await client.mutate(`
mutation {
helloWorld
}
`)
// You can also give them `DocumentNode`s
// from `graphql-tag` or equivalents
await client.query(gql`
query {
helloWorld
}
`)
// You can give variables in the second parameter options
await client.query(
`
query($foo: String!) {
hello(foo: $foo)
}
`,
{
variables: {
foo: 'bar',
},
}
)
await client.query(
`
query example {
helloExample
}
`,
{
// You can specify operation name if the queries
// are named
operationName: 'helloExample',
// Query specific headers
// These are going to be "merged" with the client set headers
headers: {
hello: 'world',
},
// Query specific cookies
// These are going to be "merged" with the client set headers
cookies: {
foo: 'bar',
},
}
)
You can change the default client headers whenever
client.setHeaders({
authorization: 'other-header',
})
You can change the default client cookies whenever
client.setCookies({
authorization: 'other-cookie',
})
If allowBatchedQueries
is set in the Mercurius registration, you can call some queries together
const batchedResponse = await client.batchQueries(
[
{
query: `
query {
helloWorld
}
`,
},
{
query: `
query($name: String!) {
user(name: $name) {
email
}
}
`,
variables: {
name: 'bob',
},
// operationName: "you-can-specify-it-here-if-needed"
},
],
// Optional
{
// Optional request specific cookies
cookies: {
foo: 'bar',
},
// Optional request specific headers
headers: {
foo: 'bar',
},
}
)
batchedResponse ===
[
{ data: { helloWorld: 'foo' } },
{ data: { user: { email: 'hello@world.com' } } },
]
If you are not already calling
.listen(PORT)
somewhere, it will automatically call it, assigning a random available port, this means you will have to manually call.close()
somewhere
.subscribe
returns a promise that resolves when the subscription connection is made
headers
&cookies
are applied the same as in.query
and.mutate
const subscription = await client.subscribe({
query: `
subscription {
notificationAdded {
id
message
}
}
`,
onData(response) {
// response.errors => array of graphql errors or undefined
response ==
{ data: { notificationAdded: { id: 1, message: 'hello world' } } }
},
// Optional
variables: { foo: 'bar' },
// Optional
operationName: 'name_if_is_named_query',
// Optional, initialization payload, usually for authorization
initPayload: { authorization: '<token>' },
// Optional, subscription specific cookies
cookies: {
authorization: '<token>',
},
// Optional, subscription specific headers
headers: {
authorization: '<token>',
},
})
// You can manually call the unsubscribe
subscription.unsubscribe()
// You will need to manually close the fastify instance somewhere
app.close()
In a federated service it's useful to test if a service is extending the entity correctly.
This function is a wrapper around
_entities
query.
An entity can be federated on a single field:
import { mercuriusFederationPlugin } from '@mercuriusjs/federation'
const schema = `
type Post @key(fields: "id") {
id: ID! @external
description: String!
}
extend type User @key(fields: "id") {
id: ID! @external
posts: [Post!]!
}
`
const app = fastify()
app.register(mercuriusFederationPlugin, {
schema,
resolvers: {
User: {
posts: () => [{ id: 'post-id', description: 'Post description' }],
},
},
})
const client = createMercuriusTestClient(app)
const entity = await client.getFederatedEntity({
typename: 'User',
keys: { id: 'user1' },
typeQuery: `
id
posts {
id
description
}`,
})
entity ===
{
__typename: 'User',
id: 'user1',
posts: [
{
id: 'post-id',
description: 'Post description',
},
],
}
or on multiple fields:
const schema = `
type ProductCategory {
id: ID!
name: String!
}
extend type Product @key(fields: "sku") @key(fields: "upc") {
upc: String! @external
sku: Int! @external
category: ProductCategory
}
`
const app = fastify()
app.register(mercuriusFederationPlugin, {
schema,
resolvers: {
Product: {
category: () => ({ id: 'product-category', name: 'Stub category' }),
},
},
})
const client = createMercuriusTestClient(app)
const entity = await client.getFederatedEntity({
typename: 'Product',
keys: { sku: 1, upc: 'upc' },
typeQuery: `
upc
sku
category {
id
name
}`,
})
entity ===
{
__typename: 'Product',
upc: 'upc',
sku: 1,
category: {
id: 'product-category',
name: 'Stub category',
},
}
const dataResponse = await client.query<{
helloWorld: string
}>(`
query {
helloWorld
}
`)
// string
dataResponse.data.helloWorld
const variablesResponse = await client.query<
{
user: {
email: string
}
},
{
name: string
}
>(
`
query($name: String!) {
user(name: $name) {
email
}
}
`,
{
variables: {
name: 'bob',
},
}
)
// string
variablesResponse.data.user.email
await client.subscribe<
{
helloWorld: string
},
{
foo: string
}
>({
query: `
subscription($foo: String!) {
helloWorld(foo: $foo)
}
`,
variables: {
// Error, Type 'number' is not assignable to type 'string'.
foo: 123,
},
onData(response) {
// string
response.data.helloWorld
},
})
MIT