v3
Plugins
Testing

Testing

Envelop comes with a dedicated package for testing purposes. You can use it to test your plugin, or your whole envelop, by replacing or mocking other plugins/phases.

The Envelop Testkit can also help you to test your envelop plugins in a headless environment, so you don’t need to deal with HTTP or anything else.

To get started with the Envelop Testkit, make sure to install it in your project (as a devDependency):

npm i -D @envelop/testing

To get started with your envelop testing, make sure first to setup your favorite test runner in your project (we use Jest).

Testing a Plugin

To test a plugin in a real GraphQL environment, just create a Testkit Envelop:

import { assertSingleExecutionValue, createTestkit } from '@envelop/testing'
import { makeExecutableSchema } from '@graphql-tools/schema'
import { useMyPlugin } from './my-plugin'
 
describe('My Plugin', () => {
  const mySchema = makeExecutableSchema({
    typeDefs: `type Query { foo: String }`,
    resolvers: {
      Query: {
        foo: () => 'bar'
      }
    }
  })
 
  it('Should run correctly with no errors', async () => {
    // Create a testkit for the plugin, using the plugin and a dummy schema
    const testkit = createTestkit([useMyPlugin], mySchema)
    // Execute the envelop using a simple query
    const result = await testkit.execute(`query testQuery { foo }`)
    // During tests, it's simpler to assume you are dealing with a non-stream responses
    assertSingleExecutionValue(result)
    // Assert that the result is correct
    expect(result.data?.foo).toBe('bar')
  })
})

Testing Envelop Instance

You can also pass a complete instance of envelop (the getEnveloped function) to your Testkit, and run the same testing flow for multiple plugins together:

import { assertSingleExecutionValue, createTestkit } from '@envelop/testing'
import { makeExecutableSchema } from '@graphql-tools/schema'
import { getEnveloped } from './my-app'
 
describe('My GQL App', () => {
  const mySchema = makeExecutableSchema({
    typeDefs: `type Query { foo: String }`,
    resolvers: {
      Query: {
        foo: () => 'bar'
      }
    }
  })
 
  it('Should run correctly with no errors', async () => {
    // Create a testkit based on the envelop, with a dummy schema
    const testkit = createTestkit(getEnveloped, mySchema)
    // Execute the envelop using a simple query
    const result = await testkit.execute(`query testQuery { foo }`)
    // During tests, it's simpler to assume you are dealing with a non-stream responses
    assertSingleExecutionValue(result)
    // Assert that the result is correct
    expect(result.data?.foo).toBe('bar')
  })
})

Additional Resources

Manipulating Envelops

If you are testing a complete envelop, you might want to manipulate the Envelop before executing it (for example, to replace a context function that authenticates the user, or to create a scenario needed for testing).

You can use modifyPlugins in order to modify the plugins that are part of your execution:

// Create a Testkit based on the Envelop, with a dummy schema
const testkit = createTestkit(getEnveloped, mySchema)
// Modify the Envelop's plugins
testkit.modifyPlugins(plugins => [...plugins, somePlugins])
// Execute the Envelop using a simple query
const result = await testkit.execute(`query testQuery { foo }`)
💡

You can either add plugins, but also replace/remove plugins that are not needed during testing.

Mocking Phases

If you need to make sure that a specific phase of envelop will perform a specific result, you can replace the entire phase with a custom one.

This is useful if you need to prepare with a specific scenario before running your Envelop, without the need to deal with specific plugins.

Here’s an example for replacing the whole context-building phase:

// Create a testkit based on the envelop, with a dummy schema
const testkit = createTestkit(getEnveloped, mySchema)
// Replace the entire context factory phase
testkit.mockPhase({
  phase: 'contextFactory',
  fn: () => ({
    currentUser: {
      id: 1
    }
  })
})
⚠️

Note: Replacing whole phases might affect plugins execution, so use it carefully!

Spying on Phases

If you are using Jest, you can use a pre-defined plugin that can help you spy on Envelop phases and check the validity of your parameters.

import { assertSingleExecutionValue, createTestkit } from '@envelop/testing'
import { getEnveloped } from './my-app'
 
describe('My GQL App', () => {
  const mySchema = buildSchema(`type Query { foo: String }`)
 
  it('Should run correctly with no errors', async () => {
    const spy = createSpiedPlugin()
    const testkit = createTestkit(getEnveloped, mySchema)
    testkit.modifyPlugins(plugins => [...plugins, spy.plugin])
    const result = await testkit.execute(`query testQuery { foo }`)
    assertSingleExecutionValue(result)
 
    // Assert and test
    expect(spy.spies.beforeContextBuilding).toHaveBeenCalledWith({
      context: expect.objectContaining({
        test: true
      }),
      extendContext: expect.any(Function)
    })
  })
})