graphql-tools icon indicating copy to clipboard operation
graphql-tools copied to clipboard

Cannot mock a field being undefined or null

Open jmtimko5 opened this issue 3 years ago • 1 comments
trafficstars

Issue workflow progress

Progress of the issue based on the Contributor Workflow

  • [ ] 1. The issue provides a reproduction available on Github, Stackblitz or CodeSandbox

    Make sure to fork this template and run yarn generate in the terminal.

    Please make sure the GraphQL Tools package versions under package.json matches yours.

  • [ ] 2. A failing test has been provided
  • [ ] 3. A local solution has been provided
  • [ ] 4. A pull request is pending review

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:

jmtimko5 avatar Oct 28 '22 00:10 jmtimko5

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

Grohden avatar Jan 18 '24 02:01 Grohden