ardatan / graphql-tools

:wrench: Utility library for GraphQL to build, stitch and mock GraphQL schemas in the SDL-first approach
https://www.graphql-tools.com
MIT License
5.34k stars 810 forks source link

Cannot mock a field being undefined or null #4792

Open jmtimko5 opened 1 year ago

jmtimko5 commented 1 year ago

Issue workflow progress

Progress of the issue based on the Contributor Workflow


Describe the bug

It seems impossible to simulate a situation where you are returned a partial set of the fields you have requested in a query. This is a very common scenario I want to test. Your UI needs to be able to handle that situation. Seemingly no matter what I try, graphql-mock returns an error when I attempt to do this. I would really appreciate help.

To Reproduce Steps to reproduce the behavior:


const typeDefs = `#graphql
type AuthConfig {
    twoFactorEnabled: Boolean
}

type OrgConfig {    
    auth: AuthConfig
    defaultSecurityPolicy: String
}

  type Query {
    getOrgConfig: OrgConfig
  }
`;

const query = /* GraphQL */ `
    query getOrgConfig {
        getOrgConfig {
            auth {
                twoFactorEnabled
            }
            defaultSecurityPolicy
        }
    }
`;

const mockSchema = addMocksToSchema({
    schema: makeExecutableSchema({
        typeDefs,
    }),

    mocks: {
        AuthConfig: () => undefined,
    },
});

graphql(mockSchema, query).then((result) => console.log("Got result", result));

Expected behavior

I expect a partial return value to be acceptable, as it is in real, not mocked, GQL.

If I try to mock AuthConfig as undefined or null I get the following

    Got result {
      errors: [
        Error: Value returned by the mock for AuthConfig is not an object.......

This make sense given the following code: https://github.com/ardatan/graphql-tools/blob/0f149dc3ac1195c4c4457f703f303e381e4fb44f/packages/mock/src/MockStore.ts#L433

My main question is how are you supposed to simulate a partial result situation if the Mocking library mandates a value for every field.

Environment:

Grohden commented 8 months ago

Nice, since 2018 without null mocks.. it's like graphql doesn't have support for null returns! (current mocks are amazing tbh, its just missing this)

I made a custom solution which I'm yet to try on a real test codebase:

const schema = buildSchema(typeDefs)
const store = createMockStore({schema})
const nullStore = {
  _nulls: {} as Record<string, Set<string | undefined>>,
  has(key: string, id: string): boolean {
    return !!this._nulls[key]?.has(id)
  },
  add(key: string, id: string) {
    if (!this._nulls[key]) {
      this._nulls[key] = new Set()
    }

    this._nulls[key].add(id)
  },
  delete(key: string, id: string) {
    if (!this._nulls[key]) {
      return
    }

    this._nulls[key].delete(id)
  },
  nullOrGet: (key: string, id: string) => {
    return nullStore.has(key, id) ? null : store.get(key, id);
  }
}

const schemaWithMocks = addMocksToSchema({
  schema,
  store,
  resolvers: () => ({
    Query: {
      fooById(_, {id}) { return nullStore.nullOrGet('Foo', id) }
    }
  })
})

const query = /* GraphQL */ `
  query Foo {
    foo(id: 1) {
      id
    }
  }
`

nullStore.add('Foo', '1')
const isNull = await graphql({ schema: schemaWithMocks, source: query })
console.log('Got result', isNull);

nullStore.delete('Foo', '1')
const isNotNull = await graphql({ schema: schemaWithMocks, source: query })
console.log('Got result', isNotNull);

Essentially, an if should be null ? then null in each Query/Mutation field resolver that I can control almost like the mock store