Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gqty needs a dynamic mocks solution. #876

Open
MarkLyck opened this issue May 20, 2022 · 3 comments
Open

gqty needs a dynamic mocks solution. #876

MarkLyck opened this issue May 20, 2022 · 3 comments
Labels
enhancement New feature or request

Comments

@MarkLyck
Copy link

MarkLyck commented May 20, 2022

I really wanted to love gqty and use it for all of my projects.

Just writing code as if it was an object and have React Suspense load it is fantastic!

But ultimately I had to switch back to Apollo hooks due to the lack of mocking abilities with gqty.

I got so far as to have a fully dynamic mock system set up with gqty using @graphql-tools's mockServer to create a schema with mocks, and relying on my testing env variable to query the mockServer instead of using the regular queryFetcher in the generated gqty file.

It wasn't easy to set up, but it worked great! It would auto mock anything I wanted using faker with a seed for consistency in tests.

However in some tests you always need to override individual results and that's where gqty fell apart for me.

Since you don't define query names when using gqty I just had no way to reasonably identify a query.

My first thought was using types, but that didn't end up being feasible especially if the same type appeared multiple times in my component.

So I thought, I could serialize the query itself and use that as a key in my mockStore, which was "okay" for simple queries.

But when I started making even minor queries I quickly ended up with this mess:

mockQuery({
      query: {
        query: {
          crm: {
            __typename: true,
            sitesConnection0: {
              __typename: true,
              totalCount: true,
              __aliasFor: 'sitesConnection',
            },
            sitesConnection1: {
              __typename: true,
              totalCount: true,
              __aliasFor: 'sitesConnection',
              __args: { condition: { active: true } },
            },
            sites0: {
              __typename: true,
              id: true,
              __aliasFor: 'sites',
              __args: { first: 1, condition: { active: true } },
            },
          },
        },
      },
      result: {
        data: {
          crm: {
            __typename: 'crmQuery',
            sitesConnection0: {
              __typename: 'SitesConnection',
              totalCount: 14413,
            },
            sitesConnection1: {
              __typename: 'SitesConnection',
              totalCount: 12081,
            },
            sites0: [{ __typename: 'Site', id: 1 }],
          },
        },
      },
    })

This technically works... but no one wants to write or see that code in your test files just to override a graphql mock.

In other graphQL libraries you would usually assign a name to a query like this:

query SiteCounts {
  crm {
    sites: sitesConnection {
      totalCount
    }
    activeSites: sitesConnection(condition: { active: true }, first: 1) {
      totalCount
      nodes {
        id
      }
    }
  }
}

and then in e.g. Apollo you can mock it like so:

const mocks = {
      SiteCounts: {
        crm: {
          sitesConnection: {
            totalCount: 0,
          },
        },
      },
    }

    renderWithRouter(
      <AutoMockedProvider mocks={mocks}>
        <Dashboard />
      </AutoMockedProvider>
    )

But that method relies on the name I gave the query SiteCounts

I'm honestly not sure what a good solution for gqty would even look like. But hopefully this can start a discussion to find a way to implement a reasonable mocking system using gqty.

If it's any help this is the mockServer I created with gqty that "worked" with the serialized queries, but stringifying the query as the key is not useable.

import { mockServer, addMocksToSchema } from '@graphql-tools/mock'
import { makeExecutableSchema } from '@graphql-tools/schema'
import { graphQlQueryToJson } from 'graphql-query-to-json'
import schemaString from '../schema.graphql?raw'

import mocks from './mocks'

// mockStore
const mockStore = new Map()
type setMockType = { query: any; result: any }
export const mockQuery = ({ query, result }: setMockType) => {
  mockStore.set(String(query), result)
}
export const resetMocks = () => {
  mockStore.clear()
}

// mockServer
const schema = makeExecutableSchema({ typeDefs: schemaString })
const schemaWithMocks = addMocksToSchema({ schema, mocks })
const server = mockServer(schemaWithMocks, mocks)

const runQuery = (query: string, variables: any) => {
  // If there's an override in mockStore, return that.
  const queryKey = JSON.stringify(graphQlQueryToJson(query))
  if (mockStore.has(queryKey)) {
    return mockStore.get(queryKey)
  }

  return server.query(query, variables)
}

export default { query: runQuery }
@MarkLyck MarkLyck changed the title Add a dynamic mocks system. gqty needs a dynamic mocks system May 20, 2022
@MarkLyck MarkLyck changed the title gqty needs a dynamic mocks system gqty needs a dynamic mocks solution. May 20, 2022
@MarkLyck
Copy link
Author

MarkLyck commented May 21, 2022

@vicary came up with the ideal solution to this problem on Discord.

This would be completely solved by adding operatioName to the useQuery.

like so:

const query = useQuery({ operationName: "SiteCounts" })

That would make it possible to easily override individual queries based on the operationName like we can with most other graphql clients 🎉

All credits to @vicary for the idea.

@Remy-T
Copy link

Remy-T commented Aug 16, 2022

bump

@vicary vicary mentioned this issue Nov 10, 2022
16 tasks
@vicary vicary added the enhancement New feature or request label Dec 4, 2022
@vicary
Copy link
Member

vicary commented May 8, 2023

@MarkLyck our new core is approaching beta from alpha. While we're updating our GitHub actions to properly release under the tag beta, please feel free to read our updated docs at https://gqty.dev and try it out!

The new useQuery and resolve supports operationName, you should be able to mock your queryFetcher like this:

const queryFetcher:QueryFetcher = async ({ query, variables, operationName }) => {
  if (operationName === 'MyTestQuery') {
    return {
      // mocked data
    };
  }
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants