redwoodjs / redwood

The App Framework for Startups
https://redwoodjs.com
MIT License
17.12k stars 980 forks source link

[RFC]: Auto Generate Fake Types from GQL Schema for Reuse in /common #7619

Open MichaelrMentele opened 1 year ago

MichaelrMentele commented 1 year ago

Summary

When #7618 is done it'd be nice to generate GQL type mocks through schema introspection for reuse across the various sides.

Motivation

You want to write unit tests. Both the web side and the API side use GQL types!

For example:

export const fakeDealTerms = <
  T extends Partial<CastFieldsToFragmentTypes<DealTerms>>
>(
  options: T = {} as T
): Required<DealTerms> & T => {
  const defaults: Required<DealTerms> = {
    __typename: 'DealTerms' as const,
    additionalProductsTotal: faker.datatype.number(),
    feesTotal: faker.datatype.number(),
    taxesTotal: faker.datatype.number(),
    taxTotals: [...Array(faker.datatype.number({ max: 5 }))].map(() =>
      faker.datatype.number()
    ),
    totalCharge: faker.datatype.number(),
    tradeInTotal: faker.datatype.number(),
  }
  return {
    ...defaults,
    ...options,
  }
}

Most of the time I simply want a nice mock that looks and feels like a type and then tailor a few of it's fields for the specific test. There is no reason we can't include these batteries out of the box.

This makes mocking GQL request often easier if you can mock a type in a single command. It also makes test writing on the frontend and backend easier.

Detailed proposal

There is a good case to be made that this should not be specific to RW because this is a general concern of all fullstack JS frameworks using Apollo however practically it's not useful without code sharing between sides so for an ideal devX this would be baked in. The implementation detail of whether their is a bit of RW specific code + a package that introspects the schema for example and generates types is not important (can always be extracted later).

Interface

Two options:

fakeGQLType('Vehicle', overrides = {})

// or just generate sigs for each type like...
fakeVehicle(overrides={})

The user can then create more specific scenarios off these base types by either doing:

const fooVehicleSituation = () => {
   // some specific setup
   fakeVehicle({ ...fooOverrides })
}

// or compose specific types via normal object composition
const barVehicleAndDeal = () => {
  return {
     ...fakeVehicle(),
     ...fakeDeal(),
  }
}

But what about correct references such as a deal having a vehicleId? If you actually care in your test then pass it in yourself. Out of scope of the framework and out of scope of the SDL to even know that. We are mocking GQL types -- not your db relations. Do that yourself.

Implementation

Milestone 1: POC with Basic tTypes

Milestone 2: Generate Mocks for Inputs etc.

Needs investigation

Are you interested in working on this?

dthyresson commented 1 year ago

In one of my office hours experiments https://github.com/redwoodjs/redwood-office-hours/tree/main/2022-10-19-snaplet-copycat-seeds I thought that Snaplet's copycat could be used for:

Thoughts @peterp ?

peterp commented 1 year ago

Totally, it generates all kinds of realistic fake data, but in a deterministic fashion rather than linearly or randomly.

For any given input it'll produce a stable output. Check it out! https://github.com/snaplet/copycat