ardeois / graphql-codegen-typescript-mock-data

[GraphQL Codegen Plugin](https://github.com/dotansimha/graphql-code-generator) for building mock data based on the schema.
MIT License
132 stars 47 forks source link

easily create deeply nested mock data by passing `overrides` to child generators #100

Closed veswill3 closed 1 year ago

veswill3 commented 1 year ago

I love the idea behind this plugin - it makes creating mock data for unit tests so much easier!

I was having some trouble getting the generator functions to create deeply complex mocks and wanted to ask if this was a feature that could be added. I would love to get involved and help with implementation if it is a good idea, but I wanted to open some dialog before trying to open a PR to make sure there are not good reasons for NOT doing these (technical or otherwise).

building from your example in the README, imagine if the User schema also had friends: [USER] (so we can show with a list)

If we want to create a complex User with deep fields present, we need to import several of the generator functions and call them when setting up overrides like this:

const user = aUser({
  login: 'mock-login',
  avatar: anAvatar({ url: 'https://www.some-image.jpg' }), // need to call anAvatar
  friends: [
    aUser({ id: 'friend-1' }), // we need to call aUser each of the three times
    aUser({ id: 'friend-2', status: Status.Offline }),
    aUser({ id: 'friend-3' }),
  ]
})

Instead, we could have the code generation functions forward overrides to the child generators something like this - totally untested, just showing the idea

export const aUser = (overrides?: Partial<User> | null): User | null => {
  // we need to check for null here, see comments below
  if (overrides === null) return null;
  return {
    id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : 'a5756f00-41a6-422a-8a7d-d13ee6a63750',
    login: overrides && overrides.hasOwnProperty('login') ? overrides.login! : 'libero',
    // overrides are passed deep.
    // * undefined --> generate like normal
    // * null --> null (see comment above)
    avatar: anAvatar(overrides?.avatar),
    // maps over overrides to generate a list of items
    // * undefined --> generate one item as normal (or many if listElementCount is set)
    // * null --> null
    friends: overrides?.friends === null ? null : overrides?.friends?.map(aUser) ?? [aUser()],
    status: overrides && overrides.hasOwnProperty('status') ? overrides.status! : Status.Online,
    updatedAt: overrides && overrides.hasOwnProperty('updatedAt') ? overrides.updatedAt! : 1458071232,
  };
};

Now generating a complex user with everything set deep looks like this

const user2 = aUser({
  login: 'mock-login',
  avatar: { url: 'https://www.some-image.jpg' },
  friends: [
    { id: 'friend-1' },
    { id: 'friend-2', status: Status.Offline },
    { id: 'friend-3' },
  ],
});

Thoughts? Again, happy to help with implementation if this is a good idea. Let me know.

stale[bot] commented 1 year ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

ardeois commented 1 year ago

Thanks for the interest on this project @veswill3 and sorry for the late reply

That's an interesting suggestion indeed, but I personally prefer to explicitly declare each generator function so we're clear what we're doing.

You suggesting would indeed generate the same output mock data, but I find it hard to understand what it will do when you read this:

const user2 = aUser({
  login: 'mock-login',
  avatar: { url: 'https://www.some-image.jpg' },
  friends: [
    { id: 'friend-1' }, // this will transform to `aUser({id: 'friend-1'})`
    { id: 'friend-2', status: Status.Offline },
    { id: 'friend-3' },
  ],
});

So this plugin will do more magic and I wonder if people will get confused? I also wonder if we are able to have strong typing on deeply nested overrides? I know we can use Partial<MyType> but would we be able to use nested Partial types?

Still this is a personal opinion, and if more people prefer this solution, I could consider changing my mind 🙂

stale[bot] commented 1 year ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

ojab commented 1 year ago

So this plugin will do more magic and I wonder if people will get confused?

I just started using graphql-codegen-typescript-mock-data (overall it's awesome, btw) and was expecting that it's working this way (i. e. already passing nested objects into mock functions).

veswill3 commented 1 year ago

Ha, yeah, I also had assumed this is how it would work.

I kinda got sidetracked with the holidays and deadlines so I have not come back to this in a while or been able to tinker around, but in addition to being able to pass deeply nested overrides, I think it would actually be even better to simply generate mock data specific to a graphQL query/mutation, since that is how it works in reality.

ardeois commented 1 year ago

@veswill3 Yes you're right it would be nice to generate types depending on document data. This is another deal, but it would be really nice to have such feature. I think a first step could be to implement this

But for your issue, I would suggest people vote on the main PR description. If we have enough votes, we could consider implementing it

stale[bot] commented 1 year ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] commented 1 year ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.