Skip to content

Commit

Permalink
Mock Data Generation
Browse files Browse the repository at this point in the history
Summary:
A new proposed module  `RelayMockPayloadGenerator` will help with mock data generation based on the FragmentReader information. This mock data later can be used in the tests for Relay Renderers and Containers.

There are two public methods:

* `RelayMockPayloadGenerator.generateVariables(...): MockData`
* `RelayMockPayloadGenerator.genedateData(...): MockData`

Those methods will create mock data and variables based on the Fragment Selection, Variables, Mock Resolvers, and actual GraphQL types information (if the schema is provided).

Basic data generation may look like this

```
const fragment = graphql`
  fragment UserFragment on User {
     id,
     name,
     profile_picture {
        uri
     }
  }
`;

const data = RelayMockPayloadGenerator.generateData(fragmentReader, /* mockResolvers, variables, schema*/);

// Generated data will have this shape:
data = {
  id: '<mock-id-1>'
  name: '<mock-value-for-"name">',
  profile_picture: {
    uri: '<mock-value-for-"uri">'
  }
}
```

Usually, on a server, you may define resolver for each individual field. We probably don't need this level of precision for data mocking. So, in our module Mock Resolvers are defined for types (Scalar and Objet).

Mock Resolver Function will receive 2 arguments:

* context - it has four properties (all nullable) - with the name of the field, alias in the selection, arguments passed to the field, and path in the selection
* generateId() - it will return incremental integer

```
type MockResolverContext = {|
  +name: ?string,
  +alias: ?string,
  +path: ?$ReadOnlyArray<string>,
  +args: ?{[string]: mixed},
|};

const data = RelayMockPayloadGenerator.generateData(fragmentReader, {
  String(context: MockResolverContext) {
    if (context.name === 'uri') {
      return 'http://test.com/test';
    }
  },
  ID(_, generateId: () => number) {
    return `my-id-${generateId()}`;
  },
  User() {
    return {
      name: 'Mark',
      emailAddresses: ['test@fb.com', 'test2fb.com'],
    };
  },
  // ...other types
});
```

In this example, for every selection with `name === 'uri'` we will return a string  `'http://test.com/test'`  and every User will have name Mark with two emailAddresses (if those will be in the selection)

In cases, when we don't have any schema information we default all scalar types (except fields with name 'id') to `String` and generating IDs for all LinkedFields, assuming object types have ids. This approach may cover many cases, especially when you don't need precise type information in your mocks. But not all.

It is possible to pass an instance of the GraphQL schema to the `RelayMockPayloadGenerator.generateData`. In this case, mock values will be generated using type information, i.e. it will generate numbers for `Int`, `Float` and `Boolean` for `Boolean`, etc. It will also check if the Object type has an ID and will generated ids for those types.

This also can be combined with the MockResolvers for types

```
const mockResolvers = {
  Int(context: MockResolverContext) {
    if (context.name === 'scale') {
        return 1;
    } else if (context.name = 'year') {
        return 2004;
    }
  },
  Float(...),
  Boolean(...),
  // ...etc
}

```

I'm planning to use this API internally in the `RelayTestWrapper` to generate mock data for Fragment Containers, exposing methods to customize generated data.

The generated payload will be published to the store and the Fragment Container will read this data from the store. So we won't actually pass the data directly to the component via props. Which should also help with testing mutations and subscriptions.

Reviewed By: josephsavona

Differential Revision: D13858667

fbshipit-source-id: a0bf48689bf3bce00ccdfdeccd351917c0d8e4f6
  • Loading branch information
alunyov authored and facebook-github-bot committed Feb 26, 2019
1 parent a1a6922 commit 09d3179
Show file tree
Hide file tree
Showing 5 changed files with 2,093 additions and 1 deletion.
1 change: 1 addition & 0 deletions packages/relay-runtime/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ module.exports = {
REFS_KEY: RelayStoreUtils.REFS_KEY,
ROOT_ID: RelayStoreUtils.ROOT_ID,
ROOT_TYPE: RelayStoreUtils.ROOT_TYPE,
TYPENAME_KEY: RelayStoreUtils.TYPENAME_KEY,

createRelayNetworkLogger: createRelayNetworkLogger,
deepFreeze: deepFreeze,
Expand Down
Loading

0 comments on commit 09d3179

Please sign in to comment.