Writing provider tests

This tutorial guides you through writing a provider test from scratch. It is a continuation of the consumer test tutorial. To start, the provider tests are written using pact-ruby. In this tutorial, you write a provider test that addresses the contract generated by discussions.spec.js.

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.

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.

require_relative '../spec_helper'

module Provider
  module DiscussionsHelper
    Pact.service_provider 'Merge Request Discussions Endpoint' do
      
    end
  end
end

The honours_pact_with block

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.

require_relative '../spec_helper'

module Provider
  module DiscussionsHelper
    Pact.service_provider 'Merge Request Discussions Endpoint' do
      honours_pact_with 'Merge Request Page' do
        
      end
    end
  end
end

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.

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
        
      end
    end
  end
end

Define the contract to verify

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.

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'
      end
    end
  end
end

Add / update the Rake tasks

Now that you have a test created, you must create Rake tasks that run this test. The Rake tasks are defined in lib/tasks/contracts.rake where we have individual Rake tasks to run individual specs, but also Rake tasks that run a group of tests.

Under the contracts:mr namespace, introduce the Rake task to run this new test specifically. In it, call pact.uri to define the location of the contract and the provider test that tests that contract. Notice here that pact_uri has a parameter called pact_helper. This is why the provider tests are called _helper.rb.

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"
  )
end

At the same time, add your new :discussions Rake task to be included in the test:merge_request Rake task. In that Rake task, there is an array defined (%w[metadata diffs]). You must add discussions in that list.

Create test data

As the last step, create the test data that allows the provider test to return the contract’s expected response. You might wonder why you create the test data last. It’s really a matter of preference. With the test already configured, you can easily run the test to verify and make sure all the necessary test data are created to produce the expected response.

You can read more about 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.

Default user in spec/contracts/provider/spec_helper.rb

Before you create the test data, note that a default user is created in the spec_helper, which is the user being used for the test runs. This user is configured using RSpec.configure, as Pact actually is built on top of RSpec. This step allows us to configure the user before any of the test runs.

RSpec.configure do |config|
  config.include Devise::Test::IntegrationHelpers
  config.before do
    user = FactoryBot.create(:user, name: "Contract Test").tap do |user|
      user.current_sign_in_at = Time.current
    end
    sign_in user
  end
end

Any further modifications to the user that’s needed can be done through the individual provider state files.

The provider_states_for block

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.

Pact.provider_states_for 'Merge Request Page' do
end

The provider_state block

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.

Pact.provider_states_for "Merge Request Page" do
  provider_state "a merge request with discussions exists" do

  end
end

The set_up block

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 to check on the status of the test and figure out what else is missing in your data setup.

Pact.provider_states_for "Merge Request Page" do
  provider_state "a merge request with discussions exists" do
    set_up do
      user = User.find_by(name: Provider::UsersHelper::CONTRACT_USER_NAME)
      namespace = create(:namespace, name: 'gitlab-org')
      project = create(:project, name: 'gitlab-qa', namespace: namespace)

      project.add_maintainer(user)

      merge_request = create(:merge_request_with_diffs, id: 1, source_project: project, author: user)

      create(:discussion_note_on_merge_request, noteable: merge_request, project: project, author: user)
    end
  end
end

Note the Provider::UsersHelper::CONTRACT_USER_NAME here to fetch a user is a user that is from the spec_helper that sets up a user before any of these tests run.

And with that, the provider tests for discussion_helper.rb should now pass with this.