diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-12-10 21:10:16 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-12-10 21:10:16 +0300 |
commit | cba8ff64401258aa68eebd7603d4022884ed0a45 (patch) | |
tree | 39d46d3dc50523a36816942b8cf079daf93dfb70 /doc/development/api_graphql_styleguide.md | |
parent | 8f143a46faf2e7b594301512757edf372c294a0c (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'doc/development/api_graphql_styleguide.md')
-rw-r--r-- | doc/development/api_graphql_styleguide.md | 78 |
1 files changed, 64 insertions, 14 deletions
diff --git a/doc/development/api_graphql_styleguide.md b/doc/development/api_graphql_styleguide.md index 8a3fe93c4b1..4e3b505d5d4 100644 --- a/doc/development/api_graphql_styleguide.md +++ b/doc/development/api_graphql_styleguide.md @@ -785,7 +785,7 @@ end You should never re-use resolvers directly. Resolvers have a complex life-cycle, with authorization, readiness and resolution orchestrated by the framework, and at -each stage lazy values can be returned to take advantage of batching +each stage [lazy values](#laziness) can be returned to take advantage of batching opportunities. Never instantiate a resolver or a mutation in application code. Instead, the units of code reuse are much the same as in the rest of the @@ -1262,7 +1262,7 @@ should look like this: ### Mounting the mutation To make the mutation available it must be defined on the mutation -type that lives in `graphql/types/mutation_types`. The +type that is stored in `graphql/types/mutation_types`. The `mount_mutation` helper method defines a field based on the GraphQL-name of the mutation: @@ -1532,32 +1532,66 @@ field :created_at, Types::TimeType, null: true, description: 'Timestamp of when ## Testing -_full stack_ tests for a graphql query or mutation live in +### Writing unit tests + +Before creating unit tests, review the following examples: + +- [`spec/graphql/resolvers/users_resolver_spec.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/spec/graphql/resolvers/users_resolver_spec.rb) +- [`spec/graphql/mutations/issues/create_spec.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/spec/graphql/mutations/issues/create_spec.rb) + +It's faster to test as much of the logic from your GraphQL queries and mutations +with unit tests, which are stored in `spec/graphql`. + +Use unit tests to verify that: + +- Types have the expected fields. +- Resolvers and mutations apply authorizations and return expected data. +- Edge cases are handled correctly. + +### Writing integration tests + +Integration tests check the full stack for a GraphQL query or mutation and are stored in `spec/requests/api/graphql`. -When adding a query, the `a working graphql query` shared example can -be used to test if the query renders valid results. +For speed, you should test most logic in unit tests instead of integration tests. +However, integration tests that check if data is returned verify the following +additional items: + +- The mutation is actually queryable within the schema (was mounted in `MutationType`). +- The data returned by a resolver or mutation correctly matches the + [return types](https://graphql-ruby.org/fields/introduction.html#field-return-type) of + the fields and resolves without errors. -Using the `GraphqlHelpers#all_graphql_fields_for`-helper, a query -including all available fields can be constructed. This makes it easy -to add a test rendering all possible fields for a query. +Integration tests can also verify the following items, because they invoke the +full stack: + +- An argument or scalar's [`prepare`](#validating-arguments) applies correctly. +- Logic in a resolver or mutation's [`#ready?` method](#correct-use-of-resolverready) applies correctly. +- An [argument's `default_value`](https://graphql-ruby.org/fields/arguments.html) applies correctly. +- Objects resolve performantly and there are no N+1 issues. + +When adding a query, you can use the `a working graphql query` shared example to test if the query +renders valid results. + +You can construct a query including all available fields using the `GraphqlHelpers#all_graphql_fields_for` +helper. This makes it easy to add a test rendering all possible fields for a query. If you're adding a field to a query that supports pagination and sorting, visit [Testing](graphql_guide/pagination.md#testing) for details. -To test GraphQL mutation requests, `GraphqlHelpers` provides 2 +To test GraphQL mutation requests, `GraphqlHelpers` provides two helpers: `graphql_mutation` which takes the name of the mutation, and a hash with the input for the mutation. This returns a struct with a mutation query, and prepared variables. -This struct can then be passed to the `post_graphql_mutation` helper, +You can then pass this struct to the `post_graphql_mutation` helper, that posts the request with the correct parameters, like a GraphQL client would do. -To access the response of a mutation, the `graphql_mutation_response` -helper is available. +To access the response of a mutation, you can use the `graphql_mutation_response` +helper. -Using these helpers, we can build specs like this: +Using these helpers, you can build specs like this: ```ruby let(:mutation) do @@ -1629,7 +1663,7 @@ end - Mimic the folder structure of `app/graphql/types`: For example, tests for fields on `Types::Ci::PipelineType` - in `app/graphql/types/ci/pipeline_type.rb` should live in + in `app/graphql/types/ci/pipeline_type.rb` should be stored in `spec/requests/api/graphql/ci/pipeline_spec.rb` regardless of the query being used to fetch the pipeline data. @@ -1702,3 +1736,19 @@ For guidance, see the [GraphQL API](documentation/graphql_styleguide.md) page. ## Include a changelog entry All client-facing changes **must** include a [changelog entry](changelog.md). + +## Laziness + +One important technique unique to GraphQL for managing performance is +using **lazy** values. Lazy values represent the promise of a result, +allowing their action to be run later, which enables batching of queries in +different parts of the query tree. The main example of lazy values in our code is +the [GraphQL BatchLoader](graphql_guide/batchloader.md). + +To manage lazy values directly, read `Gitlab::Graphql::Lazy`, and in +particular `Gitlab::Graphql::Laziness`. This contains `#force` and +`#delay`, which help implement the basic operations of creation and +elimination of laziness, where needed. + +For dealing with lazy values without forcing them, use +`Gitlab::Graphql::Lazy.with_value`. |