Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-07-20 18:40:28 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-07-20 18:40:28 +0300
commitb595cb0c1dec83de5bdee18284abe86614bed33b (patch)
tree8c3d4540f193c5ff98019352f554e921b3a41a72 /doc/development/testing_guide/contract
parent2f9104a328fc8a4bddeaa4627b595166d24671d0 (diff)
Add latest changes from gitlab-org/gitlab@15-2-stable-eev15.2.0-rc42
Diffstat (limited to 'doc/development/testing_guide/contract')
-rw-r--r--doc/development/testing_guide/contract/consumer_tests.md44
-rw-r--r--doc/development/testing_guide/contract/index.md39
-rw-r--r--doc/development/testing_guide/contract/provider_tests.md77
3 files changed, 116 insertions, 44 deletions
diff --git a/doc/development/testing_guide/contract/consumer_tests.md b/doc/development/testing_guide/contract/consumer_tests.md
index b4d6882a655..df7c9ee0abd 100644
--- a/doc/development/testing_guide/contract/consumer_tests.md
+++ b/doc/development/testing_guide/contract/consumer_tests.md
@@ -10,13 +10,15 @@ This tutorial guides you through writing a consumer test from scratch. To start,
## Create the skeleton
-Start by creating the skeleton of a consumer test. Create a file under `spec/contracts/consumer/specs` called `discussions.spec.js`.
+Start by creating the skeleton of a consumer test. Create a file under `spec/contracts/consumer/specs/project/merge_request` called `discussions.spec.js`.
Then, populate it with the following function and parameters:
- [`pactWith`](#the-pactwith-function)
- [`PactOptions`](#the-pactoptions-parameter)
- [`PactFn`](#the-pactfn-parameter)
+To learn more about how the contract test directory is structured, see the contract testing [test suite folder structure](index.md#test-suite-folder-structure).
+
### The `pactWith` function
The Pact consumer test is defined through the `pactWith` function that takes `PactOptions` and the `PactFn`.
@@ -36,15 +38,17 @@ const { pactWith } = require('jest-pact');
pactWith(
{
- consumer: 'Merge Request Page',
+ consumer: 'MergeRequest#show',
provider: 'Merge Request Discussions Endpoint',
log: '../logs/consumer.log',
- dir: '../contracts',
+ dir: '../contracts/project/merge_request/show',
},
PactFn
);
```
+To learn more about how to name the consumers and providers, see contract testing [naming conventions](index.md#naming-conventions).
+
### The `PactFn` parameter
The `PactFn` is where your tests are defined. This is where you set up the mock provider and where you can use the standard Jest methods like [`Jest.describe`](https://jestjs.io/docs/api#describename-fn), [`Jest.beforeEach`](https://jestjs.io/docs/api#beforeeachfn-timeout), and [`Jest.it`](https://jestjs.io/docs/api#testname-fn-timeout). For more information, see [https://jestjs.io/docs/api](https://jestjs.io/docs/api).
@@ -54,20 +58,20 @@ const { pactWith } = require('jest-pact');
pactWith(
{
- consumer: 'Merge Request Page',
+ consumer: 'MergeRequest#show',
provider: 'Merge Request Discussions Endpoint',
log: '../logs/consumer.log',
dir: '../contracts',
},
(provider) => {
- describe('Discussions Endpoint', () => {
+ describe('Merge Request Discussions Endpoint', () => {
beforeEach(() => {
-
+
});
it('return a successful body', () => {
-
+
});
});
},
@@ -93,14 +97,14 @@ const { Matchers } = require('@pact-foundation/pact');
pactWith(
{
- consumer: 'Merge Request Page',
+ consumer: 'MergeRequest#show',
provider: 'Merge Request Discussions Endpoint',
log: '../logs/consumer.log',
- dir: '../contracts',
+ dir: '../contracts/project/merge_request/show',
},
(provider) => {
- describe('Discussions Endpoint', () => {
+ describe('Merge Request Discussions Endpoint', () => {
beforeEach(() => {
const interaction = {
state: 'a merge request with discussions exists',
@@ -144,7 +148,7 @@ Notice how we use `Matchers` in the `body` of the expected response. This allows
After the mock provider is set up, you can write the test. For this test, you make a request and expect a particular response.
-First, set up the client that makes the API request. To do that, either create or find an existing file under `spec/contracts/consumer/endpoints` and add the following API request.
+First, set up the client that makes the API request. To do that, create `spec/contracts/consumer/endpoints/project/merge_requests.js` and add the following API request.
```javascript
const axios = require('axios');
@@ -169,18 +173,18 @@ After that's set up, import it to the test file and call it to make the request.
const { pactWith } = require('jest-pact');
const { Matchers } = require('@pact-foundation/pact');
-const { getDiscussions } = require('../endpoints/merge_requests');
+const { getDiscussions } = require('../endpoints/project/merge_requests');
pactWith(
{
- consumer: 'Merge Request Page',
+ consumer: 'MergeRequest#show',
provider: 'Merge Request Discussions Endpoint',
log: '../logs/consumer.log',
- dir: '../contracts',
+ dir: '../contracts/project/merge_request/show',
},
(provider) => {
- describe('Discussions Endpoint', () => {
+ describe('Merge Request Discussions Endpoint', () => {
beforeEach(() => {
const interaction = {
state: 'a merge request with discussions exists',
@@ -230,7 +234,7 @@ There we have it! The consumer test is now set up. You can now try [running this
As you may have noticed, the request and response definitions can get large. This results in the test being difficult to read, with a lot of scrolling to find what you want. You can make the test easier to read by extracting these out to a `fixture`.
-Create a file under `spec/contracts/consumer/fixtures` called `discussions.fixture.js`. You place the `request` and `response` definitions here.
+Create a file under `spec/contracts/consumer/fixtures/project/merge_request` called `discussions.fixture.js` where you will place the `request` and `response` definitions.
```javascript
const { Matchers } = require('@pact-foundation/pact');
@@ -274,18 +278,18 @@ With all of that moved to the `fixture`, you can simplify the test to the follow
const { pactWith } = require('jest-pact');
const { Discussions } = require('../fixtures/discussions.fixture');
-const { getDiscussions } = require('../endpoints/merge_requests');
+const { getDiscussions } = require('../endpoints/project/merge_requests');
pactWith(
{
- consumer: 'Merge Request Page',
+ consumer: 'MergeRequest#show',
provider: 'Merge Request Discussions Endpoint',
log: '../logs/consumer.log',
- dir: '../contracts',
+ dir: '../contracts/project/merge_request/show',
},
(provider) => {
- describe('Discussions Endpoint', () => {
+ describe('Merge Request Discussions Endpoint', () => {
beforeEach(() => {
const interaction = {
state: 'a merge request with discussions exists',
diff --git a/doc/development/testing_guide/contract/index.md b/doc/development/testing_guide/contract/index.md
index 6556bd85624..8e12eea2874 100644
--- a/doc/development/testing_guide/contract/index.md
+++ b/doc/development/testing_guide/contract/index.md
@@ -37,3 +37,42 @@ rake contracts:mr:pact:verify:discussions # Verify provider against the
rake contracts:mr:pact:verify:metadata # Verify provider against the consumer pacts for metadata
rake contracts:mr:test:merge_request[contract_mr] # Run all merge request contract tests
```
+
+## Test suite folder structure and naming conventions
+
+To keep the consumer and provider test suite organized and maintainable, it's important that tests are organized, also that consumers and providers are named consistently. Therefore, it's important to adhere to the following conventions.
+
+### Test suite folder structure
+
+Having an organized and sensible folder structure for the test suite makes it easier to find relevant files when reviewing, debugging, or introducing tests.
+
+#### Consumer tests
+
+The consumer tests are grouped according to the different pages in the application. Each file contains various types of requests found in a page. As such, the consumer test files are named using the Rails standards of how pages are referenced. For example, the project pipelines page would be the `Project::Pipeline#index` page so the equivalent consumer test would be located in `consumer/specs/project/pipelines/index.spec.js`.
+
+When defining the location to output the contract generated by the test, we want to follow the same file structure which would be `contracts/project/pipelines/` for this example. This is the structure in `consumer/endpoints` and `consumer/fixtures` as well.
+
+#### Provider tests
+
+The provider tests are grouped similarly to our controllers. Each of these tests contains various tests for an API endpoint. For example, the API endpoint to get a list of pipelines for a project would be located in `provider/pact_helpers/project/pipelines/get_list_project_pipelines_helper.rb`. The provider states are structured the same way.
+
+### Naming conventions
+
+When writing the consumer and provider tests, there are parts where a name is required for the consumer and provider. Since there are no restrictions imposed by Pact on how these should be named, a naming convention is important to keep it easy for us to figure out which consumer and provider tests are involved during debugging. Pact also uses the consumer and provider names to generate the generated contracts in the `#{consumer_name}-#{provider_name}` format.
+
+#### Consumer naming
+
+As mentioned in the [folder structure section](#consumer-tests), consumer tests are grouped according to the different pages in the application. As such, consumer names should follow the same naming format using the Rails standard. For example, the consumer test for `Project::Pipeline#index` would be `ProjectPipeline#index` as the consumer name. Since Pact uses this name to name the contracts it generates, the colons (`::`) are dropped as colons are not valid characters in file names.
+
+#### Provider naming
+
+These are the API endpoints that provides the data to the consumer so they are simply named according to the API endpoint they pertain to. Be mindful that this name is as descriptive as possible. For example, if we're writing a test for the `GET /groups/:id/projects` endpoint, we don't want to simply name it "Projects endpoint" as there is a `GET /projects` endpoint as well that also fetches a list of projects the user has access to across all of GitLab. An easy way to name them is by checking out our [API documentation](../../../api/api_resources.md) and naming it the same way it is named in there. So the [`GET /groups/:id/projects`](../../../api/groups.md#list-a-groups-projects) would be called `List a group’s projects` and [`GET /projects`](../../../api/projects.md#list-all-projects) would be called `List all projects`. Subsequently, the test files are named `list_a_groups_projects_helper.rb` and `list_all_projects_helper.rb` respectively.
+
+There are some cases where the provider being tested may not be documented so, in those cases, fall back to choosing a name that is as descriptive as possible to ensure it's easy to tell what the provider is for.
+
+#### Conventions summary
+
+| Tests | Folder structure | Naming convention |
+| ----- | ---------------- | ----------------- |
+| Consumer Test | Follows the Rails reference standards. For example, `Project::Pipeline#index` would be `consumer/specs/project/pipelines/index.spec.js` | Follows the Rails naming standard. For example, `Project::Pipeline#index` would be `ProjectPipeline#index` |
+| Provider Test | Grouped like the Rails controllers. For example, [`List project pipelines` API endpoint](../../../api/pipelines.md#list-project-pipelines) would be `provider/pact_helpers/project/pipelines/provider/pact_helpers/project/pipelines/get_list_project_pipelines_helper.rb` | Follows the API documentation naming scheme. For example, [`GET /projects/:id/pipelines`](../../../api/pipelines.md#list-project-pipelines) would be called `List project pipelines`. |
diff --git a/doc/development/testing_guide/contract/provider_tests.md b/doc/development/testing_guide/contract/provider_tests.md
index 0da5bcb4aef..92ac4c4ed71 100644
--- a/doc/development/testing_guide/contract/provider_tests.md
+++ b/doc/development/testing_guide/contract/provider_tests.md
@@ -6,23 +6,25 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Writing provider tests
-This tutorial guides you through writing a provider test from scratch. It is a continuation of the [consumer test tutorial](consumer_tests.md). To start, the provider tests are written using [`pact-ruby`](https://github.com/pact-foundation/pact-ruby). In this tutorial, you write a provider test that addresses the contract generated by `discussions.spec.js`.
+This tutorial guides you through writing a provider test from scratch. It is a continuation of the [consumer test tutorial](consumer_tests.md). To start, the provider tests are written using [`pact-ruby`](https://github.com/pact-foundation/pact-ruby). In this tutorial, you write a provider test that addresses the contract generated by `discussions.spec.js`. As Pact is a consumer-driven testing tool, this tutorial assumes that there is an existing consumer test that had already generated a contract for us to work with.
## Create the skeleton
-Provider tests are quite simple. The goal is to set up the test data and then link that with the corresponding contract. Start by creating a file called `discussions_helper.rb` under `spec/contracts/provider/specs`. Note that the files are called `helpers` to match how they are called by Pact in the Rake tasks, which are set up at the end of this tutorial.
+Provider tests are quite simple. The goal is to set up the test data and then link that with the corresponding contract. Start by creating a file called `discussions_helper.rb` under `spec/contracts/provider/pact_helpers/project/merge_request`. Note that the files are called `helpers` to match how they are called by Pact in the Rake tasks, which are set up at the end of this tutorial.
+
+To learn more about how the contract test directory is structured, see the contract testing [test suite folder structure](index.md#test-suite-folder-structure).
### The `service_provider` block
The `service_provider` block is where the provider test is defined. For this block, put in a description of the service provider. Name it exactly as it is called in the contracts that are derived from the consumer tests.
```ruby
-require_relative '../spec_helper'
+require_relative '../../../spec_helper'
module Provider
module DiscussionsHelper
Pact.service_provider 'Merge Request Discussions Endpoint' do
-
+
end
end
end
@@ -33,33 +35,35 @@ end
The `honours_pact_with` block describes which consumer this provider test is addressing. Similar to the `service_provider` block, name this exactly the same as it's called in the contracts that are derived from the consumer tests.
```ruby
-require_relative '../spec_helper'
+require_relative '../../../spec_helper'
module Provider
module DiscussionsHelper
Pact.service_provider 'Merge Request Discussions Endpoint' do
- honours_pact_with 'Merge Request Page' do
-
+ honours_pact_with 'MergeRequest#show' do
+
end
end
end
end
```
+To learn more about how to name the consumers and providers, see contract testing [naming conventions](index.md#naming-conventions).
+
## Configure the test app
For the provider tests to verify the contracts, you must hook it up to a test app that makes the actual request and return a response to verify against the contract. To do this, configure the `app` the test uses as `Environment::Test.app`, which is defined in [`spec/contracts/provider/environments/test.rb`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/spec/contracts/provider/environments/test.rb).
```ruby
-require_relative '../spec_helper'
+require_relative '../../../spec_helper'
module Provider
module DiscussionsHelper
Pact.service_provider 'Merge Request Discussions Endpoint' do
app { Environment::Test.app }
-
- honours_pact_with 'Merge Request Page' do
-
+
+ honours_pact_with 'MergeRequest#show' do
+
end
end
end
@@ -71,15 +75,15 @@ end
Now that the test app is configured, all that is left is to define which contract this provider test is verifying. To do this, set the `pact_uri`.
```ruby
-require_relative '../spec_helper'
+require_relative '../../../spec_helper'
module Provider
module DiscussionsHelper
Pact.service_provider 'Merge Request Discussions Endpoint' do
app { Environment::Test.app }
-
- honours_pact_with 'Merge Request Page' do
- pact_uri '../contracts/merge_request_page-merge_request_discussions_endpoint.json'
+
+ honours_pact_with 'MergeRequest#show' do
+ pact_uri '../contracts/project/merge_request/show/mergerequest#show-merge_request_discussions_endpoint.json'
end
end
end
@@ -95,8 +99,8 @@ Under the `contracts:mr` namespace, introduce the Rake task to run this new test
```ruby
Pact::VerificationTask.new(:discussions) do |pact|
pact.uri(
- "#{contracts}/contracts/merge_request_page-merge_request_discussions_endpoint.json",
- pact_helper: "#{provider}/specs/discussions_helper.rb"
+ "#{contracts}/contracts/project/merge_request/show/merge_request#show-merge_request_discussions_endpoint.json",
+ pact_helper: "#{provider}/pact_helpers/project/merge_request/discussions_helper.rb"
)
end
```
@@ -109,7 +113,7 @@ As the last step, create the test data that allows the provider test to return t
You can read more about [provider states](https://docs.pact.io/implementation_guides/ruby/provider_states). We can do global provider states but for this tutorial, the provider state is for one specific `state`.
-To create the test data, create `discussions_state.rb` under `spec/contracts/provider/states`. As a quick aside, make sure to also import this state file in the `discussions_helper.rb` file.
+To create the test data, create `discussions_state.rb` under `spec/contracts/provider/states/project/merge_request`. Be sure to also import this state file in the `discussions_helper.rb` file.
### Default user in `spec/contracts/provider/spec_helper.rb`
@@ -118,10 +122,13 @@ Before you create the test data, note that a default user is created in the [`sp
```ruby
RSpec.configure do |config|
config.include Devise::Test::IntegrationHelpers
+ config.include FactoryBot::Syntax::Methods
+
config.before do
- user = FactoryBot.create(:user, name: "Contract Test").tap do |user|
+ user = create(:user, name: Provider::UsersHelper::CONTRACT_USER_NAME).tap do |user|
user.current_sign_in_at = Time.current
end
+
sign_in user
end
end
@@ -134,7 +141,7 @@ Any further modifications to the user that's needed can be done through the indi
In the state file, you must define which consumer this provider state is for. You can do that with `provider_states_for`. Make sure that the `name` provided matches the name defined for the consumer.
```ruby
-Pact.provider_states_for 'Merge Request Page' do
+Pact.provider_states_for 'MergeRequest#show' do
end
```
@@ -143,7 +150,7 @@ end
In the `provider_states_for` block, you then define the state the test data is for. These states are also defined in the consumer test. In this case, there is a `'a merge request with discussions exists'` state.
```ruby
-Pact.provider_states_for "Merge Request Page" do
+Pact.provider_states_for "MergeRequest#show" do
provider_state "a merge request with discussions exists" do
end
@@ -155,7 +162,7 @@ end
This is where you define the test data creation steps. Use `FactoryBot` to create the data. As you create the test data, you can keep [running the provider test](index.md#run-the-provider-tests) to check on the status of the test and figure out what else is missing in your data setup.
```ruby
-Pact.provider_states_for "Merge Request Page" do
+Pact.provider_states_for "MergeRequest#show" do
provider_state "a merge request with discussions exists" do
set_up do
user = User.find_by(name: Provider::UsersHelper::CONTRACT_USER_NAME)
@@ -172,6 +179,28 @@ Pact.provider_states_for "Merge Request Page" do
end
```
-Note the `Provider::UsersHelper::CONTRACT_USER_NAME` here to fetch a user is a user that is from the [`spec_helper`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/spec/contracts/provider/spec_helper.rb) that sets up a user before any of these tests run.
+## Using the test data
+
+Now that the provider state file is created, you need to import the state file to the provider test.
+
+```ruby
+# frozen_string_literal: true
+
+require_relative '../../../spec_helper'
+require_relative '../../../states/project/merge_request/discussions_state'
+
+module Provider
+ module DiscussionsHelper
+ Pact.service_provider "/merge_request/discussions" do
+ app { Environments::Test.app }
+
+ honours_pact_with 'Merge Request#show' do
+ pact_uri '../contracts/project/merge_request/show/merge_request#show-merge_request_discussions_endpoint.json'
+ end
+ end
+ end
+end
+
+```
-And with that, the provider tests for `discussion_helper.rb` should now pass with this.
+And there we have it. The provider test for `discussions_helper.rb` should now pass with this.