When writing a lot of small services, testing the interactions between these becomes a major headache. That's the problem Pact is trying to solve.
Integration tests typically are slow and brittle, requiring each component to have it's own environment to run the tests in. With a micro-service architecture, this becomes even more of a problem. They also have to be 'all-knowing' and this makes them difficult to keep from being fragile.
After J. B. Rainsberger's talk "Integrated Tests Are A Scam" people have been thinking how to get the confidence we need to deploy our software to production without having a tiresome integration test suite that does not give us all the coverage we think it does.
Pact is a testing framework that allows you to define a pact between service consumers and providers. It provides a DSL for service consumers to define the request they will make to a service producer and the response they expect back. This expectation is used in the consumers specs to provide a mock producer, and is also played back in the producer specs to ensure the producer actually does provide the response the consumer expects.
This allows you to test both sides of an integration point using fast unit tests.
You will need to have the following items installed for you to run through the workshop.
- Java 1.8
- AndroidStudio (2.2.3)
- Android SDK 25, Emulator Image and Latest build Tools
- Ruby 2.3.0
Given we have a android app that needs to make a HTTP GET request to a sinatra webapp, and requires a response in JSON format. The client would look something like:
Service.java:
public class Service implements Repository {
public interface Api {
@GET("provider.json")
Single<ServiceResponse> loadProviderJson(@Query("valid_date") String validDate);
}
private final Api api;
@Inject
public Service(@NonNull Api api) {
this.api = api;
}
@NonNull
@Override
public Single<ServiceResponse> fetchResponse(@NonNull DateTime dateTime) {
try {
return api.loadProviderJson(DateHelper.encodeDate(dateTime));
} catch (UnsupportedEncodingException e) {
return Single.error(e);
}
}
}
and the reponse ServiceResponse.java:
public class ServiceResponse {
@Json(name = "date")
private final DateTime validDate;
@Json(name = "data")
private final List<Animal> animals;
public ServiceResponse(@Nullable DateTime validDate,
@NonNull List<Animal> animals) {
this.validDate = validDate;
this.animals = animals;
}
@Nullable
public DateTime getValidDate() {
return validDate;
}
@NonNull
public List<Animal> getAnimals() {
return animals;
}
...
and the provider provider.rb:
class Provider < Sinatra::Base
get '/provider.json', :provides => 'json' do
valid_time = Time.parse(params[:valid_date])
JSON.pretty_generate({
:test => 'NO',
:valid_date => DateTime.now,
:animals => ProviderData.animals
})
end
end
Now lets test the client on the app:
ServiceTest.java:
public class ServiceTest {
Service.Api api;
Service service;
@Before
public void setup() {
api = mock(Service.Api.class);
service = new Service(api);
}
@Test
public void should_process_json_payload_from_provider() {
// given
ServiceResponse response = ServiceResponse.create(DateTime.now(), Collections.singletonList(Animal.create("Doggy", "dog")));
when(api.loadProviderJson(any())).thenReturn(Single.just(response));
// when
TestObserver<ServiceResponse> observer = service.fetchResponse(DateTime.now()).test();
// then
observer.assertNoErrors();
observer.assertValue(response);
}
}
Let's run this test and see it all pass:
$ ./gradlew clean testDebugUnitTest
...
:app:testDebugUnitTest
au.com.dius.pactconsumer.domain.PresenterTest > should_show_empty_when_fetch_returns_nothing PASSED
au.com.dius.pactconsumer.domain.PresenterTest > should_show_error_when_fetch_fails PASSED
au.com.dius.pactconsumer.domain.PresenterTest > should_show_loaded_when_fetch_succeeds PASSED
au.com.dius.pactconsumer.domain.PresenterTest > should_show_loading_when_fetching PASSED
au.com.dius.pactconsumer.data.ServiceTest > should_process_json_payload_from_provider PASSED
au.com.dius.pactconsumer.data.FakeServiceTest > should_return_list_of_animals PASSED
BUILD SUCCESSFUL
Total time: 16.089 secs
However, there is a problem with this integration point. The provider returns a different field names, which will blow up when run for real even with the tests all passing. Here is where Pact comes in.
Lets setup Pact in the consumer. Pact lets the consumers define the expectations for the integration point.
Lets add a pact for the consumer.
ServicePactTest.java:
public class ServicePactTest {
static final DateTime DATE_TIME;
static final Map<String, String> HEADERS;
static final String JSON;
static {
DATE_TIME = DateTime.now();
HEADERS = new HashMap<>();
HEADERS.put("Content-Type", "application/json");
JSON = "{\n" +
" \"test\": \"NO\",\n" +
" \"date\": \"" + DateHelper.toString(DATE_TIME) + "\",\n" +
" \"data\": [\n" +
" {\n" +
" \"name\": \"Doggy\",\n" +
" \"image\": \"dog\"\n" +
" }\n" +
" ]\n" +
"}";
}
Service service;
@Before
public void setUp() {
NetworkModule networkModule = new NetworkModule();
service = new Service(networkModule.getRetrofit(mock(Context.class), "http://localhost:9292").create(Service.Api.class));
}
@Rule
public PactProviderRule mockProvider = new PactProviderRule("our_provider", "localhost", 9292, this);
@Pact(provider = "our_provider", consumer = "our_consumer")
public PactFragment createFragment(PactDslWithProvider builder) throws UnsupportedEncodingException {
return builder
.given("data count is > 0")
.uponReceiving("a request for json data")
.path("/provider.json")
.method("GET")
.query("valid_date=" + DateHelper.encodeDate(DATE_TIME))
.willRespondWith()
.status(200)
.headers(HEADERS)
.body(JSON)
.toFragment();
}
@Test
@PactVerification("our_provider")
public void should_process_the_json_payload_from_provider() {
TestObserver<ServiceResponse> observer = service.fetchResponse(DATE_TIME).test();
observer.assertNoErrors();
observer.assertValue(ServiceResponse.create(DATE_TIME, Collections.singletonList(Animal.create("Doggy", "dog"))));
}
}
Running this test still passes, but it creates a pact file which we can use to validate our assumptions on the provider side.
./gradlew clean testDebugUnitTest
...
au.com.dius.pactconsumer.domain.PresenterTest > should_show_empty_when_fetch_returns_nothing PASSED
au.com.dius.pactconsumer.domain.PresenterTest > should_show_error_when_fetch_fails PASSED
au.com.dius.pactconsumer.domain.PresenterTest > should_show_loaded_when_fetch_succeeds PASSED
au.com.dius.pactconsumer.domain.PresenterTest > should_show_loading_when_fetching PASSED
au.com.dius.pactconsumer.data.ServiceTest > should_process_json_payload_from_provider PASSED
au.com.dius.pactconsumer.data.FakeServiceTest > should_return_list_of_animals PASSED
au.com.dius.pactconsumer.data.ServicePactTest > should_process_the_json_payload_from_provider PASSED
BUILD SUCCESSFUL
Total time: 18.835 secs
Generated pact file (consumer/app/target/pacts/our_consumer-our_provider.json):
{
"provider": {
"name": "our_provider"
},
"consumer": {
"name": "our_consumer"
},
"interactions": [
{
"description": "a request for json data",
"request": {
"method": "GET",
"path": "/provider.json",
"query": "valid_date=2017-02-01T19%253A53%253A27.038%252B11%253A00"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"body": {
"data": [
{
"image": "dog",
"name": "Doggy"
}
],
"date": "2017-02-01T19:53:27.038+11:00",
"test": "NO"
}
},
"providerState": "data count is > 0"
}
],
"metadata": {
"pact-specification": {
"version": "2.0.0"
},
"pact-jvm": {
"version": "3.3.6"
}
}
}
Pact has a rake task to verify the producer against the generated pact file. It can get the pact file from any URL (like the last successful CI build), but we just going to use the local one. Here is the addition to the Rakefile.
Rakefile:
require 'pact/tasks'
spec/pact_helper.rb:
require 'pact/provider/rspec'
Pact.service_provider "our_provider" do
honours_pact_with 'our_consumer' do
pact_uri 'spec/pacts/our_consumer-our_provider.json'
end
end
Now if we copy the pact file from the consumer project and run our pact verification task, it should fail.
$ rake pact:verify
SPEC_OPTS='' /home/theeban/.rvm/rubies/ruby-2.3.0/bin/ruby -S pact verify --pact-helper /home/theeban/Projects/pact-workshop-android/provider/spec/pact_helper.rb
Reading pact at spec/pacts/our_consumer-our_provider.json
Verifying a pact between our_consumer and our_provider
Given data count is > 0
a request for json data
with GET /provider.json?valid_date=2017-02-01T19%253A53%253A27.038%252B11%253A00
returns a response which
has status code 200 (FAILED - 1)
has a matching body (FAILED - 2)
includes headers
"Content-Type" with value "application/json" (FAILED - 3)
Failures:
1) Verifying a pact between our_consumer and our_provider Given data count is > 0 a request for json data with GET /provider.json?valid_date=2017-02-01T19%253A53%253A27.038%252B11%253A00 returns a response which has status code 200
Got 0 failures and 2 other errors:
1.1) Failure/Error: set_up_provider_state interaction.provider_state, options[:consumer]
RuntimeError:
Could not find provider state "data count is > 0" for consumer our_consumer
# /home/theeban/.rvm/gems/ruby-2.3.0@example_pact/gems/pact-1.9.0/bin/pact:4:in `<top (required)>'
# /home/theeban/.rvm/gems/ruby-2.3.0@example_pact/bin/pact:22:in `load'
# /home/theeban/.rvm/gems/ruby-2.3.0@example_pact/bin/pact:22:in `<main>'
1.2) Failure/Error: tear_down_provider_state interaction.provider_state, options[:consumer]
RuntimeError:
Could not find provider state "data count is > 0" for consumer our_consumer
# /home/theeban/.rvm/gems/ruby-2.3.0@example_pact/gems/pact-1.9.0/bin/pact:4:in `<top (required)>'
# /home/theeban/.rvm/gems/ruby-2.3.0@example_pact/bin/pact:22:in `load'
# /home/theeban/.rvm/gems/ruby-2.3.0@example_pact/bin/pact:22:in `<main>'
2) Verifying a pact between our_consumer and our_provider Given data count is > 0 a request for json data with GET /provider.json?valid_date=2017-02-01T19%253A53%253A27.038%252B11%253A00 returns a response which has a matching body
Got 0 failures and 2 other errors:
2.1) Failure/Error: set_up_provider_state interaction.provider_state, options[:consumer]
RuntimeError:
Could not find provider state "data count is > 0" for consumer our_consumer
# /home/theeban/.rvm/gems/ruby-2.3.0@example_pact/gems/pact-1.9.0/bin/pact:4:in `<top (required)>'
# /home/theeban/.rvm/gems/ruby-2.3.0@example_pact/bin/pact:22:in `load'
# /home/theeban/.rvm/gems/ruby-2.3.0@example_pact/bin/pact:22:in `<main>'
2.2) Failure/Error: tear_down_provider_state interaction.provider_state, options[:consumer]
RuntimeError:
Could not find provider state "data count is > 0" for consumer our_consumer
# /home/theeban/.rvm/gems/ruby-2.3.0@example_pact/gems/pact-1.9.0/bin/pact:4:in `<top (required)>'
# /home/theeban/.rvm/gems/ruby-2.3.0@example_pact/bin/pact:22:in `load'
# /home/theeban/.rvm/gems/ruby-2.3.0@example_pact/bin/pact:22:in `<main>'
3) Verifying a pact between our_consumer and our_provider Given data count is > 0 a request for json data with GET /provider.json?valid_date=2017-02-01T19%253A53%253A27.038%252B11%253A00 returns a response which includes headers "Content-Type" with value "application/json"
Got 0 failures and 2 other errors:
3.1) Failure/Error: set_up_provider_state interaction.provider_state, options[:consumer]
RuntimeError:
Could not find provider state "data count is > 0" for consumer our_consumer
# /home/theeban/.rvm/gems/ruby-2.3.0@example_pact/gems/pact-1.9.0/bin/pact:4:in `<top (required)>'
# /home/theeban/.rvm/gems/ruby-2.3.0@example_pact/bin/pact:22:in `load'
# /home/theeban/.rvm/gems/ruby-2.3.0@example_pact/bin/pact:22:in `<main>'
3.2) Failure/Error: tear_down_provider_state interaction.provider_state, options[:consumer]
RuntimeError:
Could not find provider state "data count is > 0" for consumer our_consumer
# /home/theeban/.rvm/gems/ruby-2.3.0@example_pact/gems/pact-1.9.0/bin/pact:4:in `<top (required)>'
# /home/theeban/.rvm/gems/ruby-2.3.0@example_pact/bin/pact:22:in `load'
# /home/theeban/.rvm/gems/ruby-2.3.0@example_pact/bin/pact:22:in `<main>'
1 interaction, 1 failure
Failed interactions:
bundle exec rake pact:verify:at[spec/pacts/our_consumer-our_provider.json] PACT_DESCRIPTION="a request for json data" PACT_PROVIDER_STATE="data count is > 0" # A request for json data given data count is > 0
For assistance debugging failures, run `bundle exec rake pact:verify:help`
Could not find one or more provider states.
Have you required the provider states file for this consumer in your pact_helper.rb?
If you have not yet defined these states, here is a template:
Pact.provider_states_for "our_consumer" do
provider_state "data count is > 0" do
set_up do
# Your set up code goes here
end
end
end
This has failed due to the provider state we defined. Luckily pact has been quite helpful and given us a snippet of what we need to do to fix it.
Add the snippet from the verification failure to the pact helper.
spec/pact_helper.rb:
Pact.provider_states_for "our_consumer" do
provider_state "data count is > 0" do
set_up do
# Your set up code goes here
end
end
end
and then re-run the provider verification.
$ rake pact:verify
SPEC_OPTS='' /home/theeban/.rvm/rubies/ruby-2.3.0/bin/ruby -S pact verify --pact-helper /home/theeban/Projects/pact-workshop-android/provider/spec/pact_helper.rb
Reading pact at spec/pacts/our_consumer-our_provider.json
Verifying a pact between our_consumer and our_provider
Given data count is > 0
a request for json data
with GET /provider.json?valid_date=2017-02-01T19%253A53%253A27.038%252B11%253A00
returns a response which
has status code 200
has a matching body (FAILED - 1)
includes headers
"Content-Type" with value "application/json"
Failures:
1) Verifying a pact between our_consumer and our_provider Given data count is > 0 a request for json data with GET /provider.json?valid_date=2017-02-01T19%253A53%253A27.038%252B11%253A00 returns a response which has a matching body
Failure/Error: expect(response_body).to match_term expected_response_body, diff_options
Actual: {"test":"NO","valid_date":"2017-02-01T20:14:51+11:00","animals":[{"name":"Buddy","image":"dog"},{"name":"Cathy","image":"cat"},{"name":"Birdy","image":"bird"}]}
@@ -1,10 +1,3 @@
{
- "data": [
- {
- "image": "dog",
- "name": "Doggy"
- },
- ],
- "date": "2017-02-01T19:53:27.038+11:00"
}
Key: - means "expected, but was not found".
+ means "actual, should not be found".
Values where the expected matches the actual are not shown.
# /home/theeban/.rvm/gems/ruby-2.3.0@example_pact/gems/pact-1.9.0/bin/pact:4:in `<top (required)>'
# /home/theeban/.rvm/gems/ruby-2.3.0@example_pact/bin/pact:22:in `load'
# /home/theeban/.rvm/gems/ruby-2.3.0@example_pact/bin/pact:22:in `<main>'
1 interaction, 1 failure
Failed interactions:
bundle exec rake pact:verify:at[spec/pacts/our_consumer-our_provider.json] PACT_DESCRIPTION="a request for json data" PACT_PROVIDER_STATE="data count is > 0" # A request for json data given data count is > 0
For assistance debugging failures, run `bundle exec rake pact:verify:help`
The test has failed for 2 reasons. Firstly, the animals field has a different value to what was expected by the consumer. Secondly, and more importantly, the consumer was expecting a date field.
Let's correct the consumer tests to handle any list of animals and use the correct field for the date.
Then we need to add a type matcher for animals
and change the field for the date to be valid_date
. We can also
add a regular expression to make sure the valid_date
field is a valid date. This is important because we are
parsing it.
The updated consumer test is now:
public class ServicePactTest {
static final DateTime DATE_TIME;
static final Map<String, String> HEADERS;
static {
DATE_TIME = DateTime.now();
HEADERS = new HashMap<>();
HEADERS.put("Content-Type", "application/json");
}
Service service;
@Before
public void setUp() {
NetworkModule networkModule = new NetworkModule();
service = new Service(networkModule.getRetrofit(mock(Context.class), "http://localhost:9292").create(Service.Api.class));
}
@Rule
public PactProviderRule mockProvider = new PactProviderRule("our_provider", "localhost", 9292, this);
@Pact(provider = "our_provider", consumer = "our_consumer")
public PactFragment createFragment(PactDslWithProvider builder) throws UnsupportedEncodingException {
PactDslJsonBody body = new PactDslJsonBody()
.stringType("test")
.stringType("valid_date", DateHelper.toString(DATE_TIME))
.eachLike("animals", 3)
.stringType("name", "Doggy")
.stringType("image", "dog")
.closeObject()
.closeArray()
.asBody();
return builder
.given("data count is > 0")
.uponReceiving("a request for json data")
.path("/provider.json")
.method("GET")
.query("valid_date=" + DateHelper.encodeDate(DATE_TIME))
.willRespondWith()
.status(200)
.headers(HEADERS)
.body(body)
.toFragment();
}
@Test
@PactVerification("our_provider")
public void should_process_the_json_payload_from_provider() {
TestObserver<ServiceResponse> observer = service.fetchResponse(DATE_TIME).test();
observer.assertNoErrors();
observer.assertValue(ServiceResponse.create(DATE_TIME, Arrays.asList(
Animal.create("Doggy", "dog"),
Animal.create("Doggy", "dog"),
Animal.create("Doggy", "dog")
)));
}
}
Re-run the tests will now generate an updated pact file.
./gradlew clean testDebugUnitTest
...
au.com.dius.pactconsumer.domain.PresenterTest > should_show_empty_when_fetch_returns_nothing PASSED
au.com.dius.pactconsumer.domain.PresenterTest > should_show_error_when_fetch_fails PASSED
au.com.dius.pactconsumer.domain.PresenterTest > should_show_loaded_when_fetch_succeeds PASSED
au.com.dius.pactconsumer.domain.PresenterTest > should_show_loading_when_fetching PASSED
au.com.dius.pactconsumer.data.ServiceTest > should_process_json_payload_from_provider PASSED
au.com.dius.pactconsumer.data.FakeServiceTest > should_return_list_of_animals PASSED
au.com.dius.pactconsumer.data.ServicePactTest > should_process_the_json_payload_from_provider PASSED
BUILD SUCCESSFUL
Total time: 17.235 secs
Copy over the new pact file.
Running the verification against the provider now passes. Yay!
$ rake pact:verify
SPEC_OPTS='' /home/theeban/.rvm/rubies/ruby-2.3.0/bin/ruby -S pact verify --pact-helper /home/theeban/Projects/pact-workshop-android/provider/spec/pact_helper.rb
Reading pact at spec/pacts/our_consumer-our_provider.json
WARN: Ignoring unsupported matching rules {"min"=>0} for path $['body']['animals']
Verifying a pact between our_consumer and our_provider
Given data count is > 0
a request for json data
with GET /provider.json?valid_date=2017-02-01T20%253A30%253A58.233%252B11%253A00
returns a response which
has status code 200
has a matching body
includes headers
"Content-Type" with value "application/json"
1 interaction, 0 failures
In this step we are going to add a test for the case where the query parameter is missing. We do this by adding additional expectations.
ServiceMissingQueryPactTest.java:
public class ServiceMissingQueryPactTest {
Service service;
@Before
public void setUp() {
NetworkModule networkModule = new NetworkModule();
service = new Service(networkModule.getRetrofit(mock(Context.class), "http://localhost:9292").create(Service.Api.class));
}
@Rule
public PactProviderRule mockProvider = new PactProviderRule("our_provider", "localhost", 9292, this);
@Pact(provider = "our_provider", consumer = "our_consumer")
public PactFragment createFragment(PactDslWithProvider builder) throws UnsupportedEncodingException {
return builder
.given("data count is > 0")
.uponReceiving("a request with an missing date parameter")
.path("/provider.json")
.method("GET")
.willRespondWith()
.status(400)
.body("valid_date is required")
.toFragment();
}
@Test
@PactVerification("our_provider")
public void should_process_the_json_payload_from_provider() {
TestObserver<ServiceResponse> observer = service.fetchResponse(null).test();
observer.assertError(BadRequestException.class);
}
}
After running our tests, the pact file will have a new interaction.
spec/pacts/our_consumer-our_provider.json:
{
"description": "a request with an missing date parameter",
"request": {
"method": "GET",
"path": "/provider.json"
},
"response": {
"status": 400,
"body": "valid_date is required"
},
"providerState": "data count is > 0"
}
Let us run this updated pact file with our provider.
We get a lot of errors because our provider fails with a 500 status and an HTML error page. Time to update the provider to handle these cases.
lib/provider.rb:
class Provider < Sinatra::Base
get '/provider.json', :provides => 'json' do
if params[:valid_date].nil?
[400, '"valid_date is required"']
else
valid_time = Time.parse(params[:valid_date])
JSON.pretty_generate({
:test => 'NO',
:valid_date => DateTime.now,
:animals => ProviderData.animals
})
end
end
end
Now the pact verification all passes.
We have one final thing to test for. We need to handle when there is no animals. This is an important bit of information to add to our contract. Let us start with a consumer test for this.
ServiceNoContentPactTest.java:
public class ServiceNoContentPactTest {
static final DateTime DATE_TIME;
static {
DATE_TIME = DateTime.now();
}
Service service;
@Before
public void setUp() {
NetworkModule networkModule = new NetworkModule();
service = new Service(networkModule.getRetrofit(mock(Context.class), "http://localhost:9292").create(Service.Api.class));
}
@Rule
public PactProviderRule mockProvider = new PactProviderRule("our_provider", "localhost", 9292, this);
@Pact(provider = "our_provider", consumer = "our_consumer")
public PactFragment createFragment(PactDslWithProvider builder) throws UnsupportedEncodingException {
return builder
.given("data count is == 0")
.uponReceiving("a request for json data")
.path("/provider.json")
.method("GET")
.query("valid_date=" + DateHelper.encodeDate(DATE_TIME))
.willRespondWith()
.status(404)
.toFragment();
}
@Test
@PactVerification("our_provider")
public void should_process_the_json_payload_from_provider() {
TestObserver<ServiceResponse> observer = service.fetchResponse(DATE_TIME).test();
observer.assertNoErrors();
observer.assertValue(new ServiceResponse(null, Collections.emptyList()));
}
}
{
"description": "a request for json data",
"request": {
"method": "GET",
"path": "/provider.json",
"query": "valid_date=2017-02-01T20%253A50%253A45.397%252B11%253A00"
},
"response": {
"status": 404
},
"providerState": "data count is == 0"
},
To be able to verify our provider, we create a data class that the provider can use, and then set the data in the state change setup callback.
lib/provider.rb:
class Provider < Sinatra::Base
get '/provider.json', :provides => 'json' do
if params[:valid_date].nil?
[400, '"valid_date is required"']
elsif ProviderData.animals.size == 0
404
else
valid_time = Time.parse(params[:valid_date])
JSON.pretty_generate({
:test => 'NO',
:valid_date => DateTime.now,
:animals => ProviderData.animals
})
end
end
end
Now we can set the data count appropriately.
spec/pact_helper.rb:
Pact.provider_states_for "our_consumer" do
provider_state "data count is > 0" do
set_up do
ProviderData.animals = ANIMALS_LIST
end
end
provider_state "data count is == 0" do
set_up do
ProviderData.animals = []
end
end
end
Running the provider verification passes. Awesome, we are all done.
OK, so now we have our Consumer generating pacts and our Provider verifying them - awesome! But what if other teams are providing APIs for you to consumer? It's a bit cumbersome sharing files around like this.
How can we effectively collaborate on these contracts?
This is where the Pact Broker comes in, The Pact Broker enables you to share your pacts between projects. It has a bunch of features, but for now we are most interested in the fact that it lets us query and manage them via an HTTP API.
In our consumer gradle build, we've added a new task to do this.
app/build.gradle:
pact {
publish {
pactDirectory = 'app/target/pacts'
pactBrokerUrl = 'https://dXfltyFMgNOFZAxr8io9wJ37iUpY42M:O5AIZWxelWbLvqMd8PkAVycBJh2Psyg1@test.pact.dius.com.au/'
version = '1.0.0'
}
}
Run ./gradlew pactPublish
to grab all pacts from app/target/pacts
and publish them to our broker, assiging them
the version 1.0.0
.
Head over to https://test.pact.dius.com.au/ui/relationships to see a visualisation of this.
Now that we've published the contracts, in your provider build, we update the pact_helper.rb
to pull pacts from the broker instead of a local file:
spec/pact_helper.rb:
Pact.service_provider "our_provider" do
honours_pact_with 'our_consumer' do
pact_uri URI.encode('https://test.pact.dius.com.au/pact/provider/our_provider/consumer/our_consumer/latest')
end
end