npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

graphql-stateful-mock

v1.1.1

Published

Mock a GraphQL schema — but in a stateful way

Downloads

22

Readme

graphql-stateful-mock

npm version

Mock a GraphQL schema — but in a stateful way.

Very similar to GraphQL tools mocking but produce a schema that is:

  • consistent: the same query with the same arguments will, by default, return the exact same result
  • mutable: developer can implement mutations that will modify the results for other queries

Install

npm install graphql-stateful-mock -D

Usage

import { makeExecutableSchema } from '@graphql-tools/schema';
import { addMocksToSchema } from 'graphql-stateful-mock';
import { graphql } from 'graphql';

// Fill this in with the schema string
const schemaString = `...`;

// Make a GraphQL schema with no resolvers
const schema = makeExecutableSchema({ typeDefs: schemaString });

// Create a new schema with mocks
const schemaWithMocks = addMocksToSchema({ schema });

const query = `
query tasksForUser {
  user(id: 6) { id, name }
}
`;

graphql(schemaWithMocks, query).then((result) => console.log('Got result', result));

// the same query will produce the exact same result 
graphql(schemaWithMocks, query).then((result) => console.log('Got result 2n time', result));

Internally, the state is stored in a MockStore that user can use to get and set mocks values. You can initiate the store independently to access it:


// Create a MockStore for this schema
const store = createMockStore({ schema });

// Create a new schema with the mock store
const schemaWithMocks = addMocksToSchema({ schema, store });

Recipes

Customizing mocks

Use the mocks option on addMocksToSchema to customize the generated mock values.

Define mocks with one function by field:

const mocks = {
  User: {
    name: () => casual.name(),
  }
}

Or by defining one function by type:

const mocks = {
  User: () => ({
    name: casual.name(),
    age: casual.integer(0, 120),
  }),
}

Custom scalar

const mocks = {
  DateTime: () => casual.date(),
}

Lists

To define a mock for a list, simply return an empty array of the desired length as mock value for the field:

const mocks = {
  User: {
    friends: () => [...new Array(casual.integer(2, 6))],
  }
}

Optionally, you can specify field values for the elements of the list:

const mocks = {
  User: {
    friends: () => [
      ...new Array(casual.integer(2, 6))
    ]
    // all my friends are between 21 and 30
    .map(() => ({ age: casual.integer(21, 30)})),
  }
}

Abstract types

If you'd like to provide a mock for an Union or Interface type, you need to provide the type with an extra __typename.

const typeDefs = `
  ...
  union Result = User | Book
`;
const mocks = {
  Result: () => ({
    __typename: 'User',
    name: casual.name(),
  })
}

Appplying mutations

Use resolvers option of addMocksToSchema to implement custom resolvers that interact with the store, especially to mutate field values in store.

const typeDefs = `
type User {
  id: Id!
  name: String!
}
type Query {
  me: User!
}
type Mutation {
  changeMyName(newName: String!): User!
}
`
const schema = makeExecutableSchema({ typeDefs: schemaString });
const schemaWithMocks = addMocksToSchema({
  schema,
  resolvers: (store) => ({
    Mutation: {
      changeMyName: (_, { newName }) => {
        // special singleton types `Query` and `Mutation` will use the key `ROOT`

        // this will set the field value for the `User` entity referenced in field
        // `me` of the singleton `Query`
        store.set('Query', 'ROOT', 'me', { name: newName });

        return store.get('Query', 'ROOT', 'me');
      }
    }
  })
});

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:

store.set('Query', 'ROOT', 'me', { name: newName });

// is equivalent to:
const meRef = store.get('Query', 'ROOT', `me`) as Ref;
store.set(meRef, 'name', newName);

Handling *byId fields

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:

const typeDefs = `
type User {
  id: Id!
  name: String!
}
type Query {
  userById(id: ID!): User!
}
`
const schema = makeExecutableSchema({ typeDefs: schemaString });
const schemaWithMocks = addMocksToSchema({
  schema,
  store,
  resolvers: (store) => ({
    Query {
      userById(_, { id }) => store.get('User', id),
    }
  })
});

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.

Implementing a pagination

The idea is that the store contains the full list, as field value, and that the resolver queries the store and slice the results:

const typeDefs = `
type User {
  id: Id!
  name: String!
  friends(offset: Int!, limit: Int!): [User!]!
}
type Query {
  me: User!
}
`
const schema = makeExecutableSchema({ typeDefs: schemaString });
const schemaWithMocks = addMocksToSchema({
  schema,
  store,
  resolvers: (store) => ({
    User: {
      // `addMocksToSchema` resolver will pass a `Ref` as `parent`
      // it contains a key to the `User` we are dealing with
      friends: (userRef, { offset, limit }) => {
        // this will generate and store a list of `Ref`s to some `User`s
        // next time we go thru this resolver (with same parent), the list
        // will be the same
        const fullList = store.get(userRef, 'friends') as Ref[];

        // actually apply pagination slicing
        return fullList.slice(offset, offset + limit)
      }
    }
  })
});

Relay-style pagination

The principles stay the same than for basic pagination:

const typeDefs = `
type User {
  id: Id!
  name: String!
  friends(offset: Int!, limit: Int!): FriendsConnection;
}
type FriendsConnection {
  totalCount: Int!
  edges: [FriendConnectionEdge!]!
}
type FriendsConnectionEdge {
  node: User!
}
type Query {
  me: User!
}
`
const schema = makeExecutableSchema({ typeDefs: schemaString });
const store = createMockStore({ schema });
const schemaWithMocks = addMocksToSchema({
  schema,
  store,
  resolvers: (store) => ({
    User: {
      friends: (userRef, { offset, limit }) => {

        const connectionRef = store.get(userRef, 'friends', 'edges');

        return {
          totalCount: edgesFullList.length,
          edges: edgesFullList.slice(offset, offset + limit)
      }
    }
  })
});

Related

  • graphql-tools#1682: [Feature request] Mocking: access generated mock objects for individual queries and mutations.
  • fejk-ql: a stateless GraphQL mock with stateful capabilities.