pact 1.3.0 → 1.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +1 -1
- data/CHANGELOG.md +14 -0
- data/Gemfile.lock +5 -5
- data/README.md +25 -29
- data/documentation/README.md +9 -7
- data/lib/pact/configuration.rb +83 -12
- data/lib/pact/consumer/mock_service/interaction_mismatch.rb +5 -1
- data/lib/pact/consumer/spec_hooks.rb +7 -4
- data/lib/pact/consumer/world.rb +12 -0
- data/lib/pact/consumer_contract/headers.rb +51 -0
- data/lib/pact/consumer_contract/request.rb +6 -1
- data/lib/pact/provider/configuration/service_provider_dsl.rb +6 -1
- data/lib/pact/provider/matchers/messages.rb +2 -2
- data/lib/pact/provider/rspec.rb +7 -1
- data/lib/pact/provider/rspec/backtrace_formatter.rb +30 -6
- data/lib/pact/provider/rspec/matchers.rb +9 -6
- data/lib/pact/shared/json_differ.rb +15 -0
- data/lib/pact/shared/request.rb +11 -2
- data/lib/pact/shared/text_differ.rb +14 -0
- data/lib/pact/version.rb +1 -1
- data/spec/lib/pact/configuration_spec.rb +127 -5
- data/spec/lib/pact/consumer/mock_service/interaction_mismatch_spec.rb +8 -5
- data/spec/lib/pact/consumer_contract/headers_spec.rb +107 -0
- data/spec/lib/pact/consumer_contract/request_spec.rb +9 -0
- data/spec/lib/pact/matchers/matchers_spec.rb +35 -0
- data/spec/lib/pact/provider/configuration/service_provider_dsl_spec.rb +16 -0
- data/spec/lib/pact/provider/matchers/messages_spec.rb +5 -4
- data/spec/lib/pact/provider/rspec_spec.rb +15 -11
- data/spec/lib/pact/shared/json_differ_spec.rb +36 -0
- data/spec/lib/pact/shared/request_spec.rb +25 -1
- data/spec/lib/pact/shared/text_differ_spec.rb +54 -0
- data/spec/support/pact_helper.rb +2 -0
- data/spec/support/test_app_with_right_content_type_differ.json +23 -0
- data/tasks/pact-test.rake +6 -0
- metadata +72 -30
- checksums.yaml +0 -7
- data/documentation/Testing with pact.png +0 -0
- data/documentation/best-practices.md +0 -33
- data/documentation/development-workflow.md +0 -22
- data/documentation/faq.md +0 -81
- data/documentation/provider-states.md +0 -178
- data/documentation/raq.md +0 -39
- data/documentation/terminology.md +0 -25
- data/documentation/troubleshooting.md +0 -4
- data/documentation/verifying-pacts.md +0 -106
checksums.yaml
DELETED
@@ -1,7 +0,0 @@
|
|
1
|
-
---
|
2
|
-
SHA1:
|
3
|
-
metadata.gz: 344262ac8b4cc83241621231547e801f20ee20b7
|
4
|
-
data.tar.gz: 0f66fdec5c968b3711a2b1c2ea701ddcc189d7e6
|
5
|
-
SHA512:
|
6
|
-
metadata.gz: 64836e31b92b54c0d9f1edf75ada165b57fa646d45467872b6561721c4fd963176d52c7dab8474bd0490a2b33de418a57b82292fcb8adcf72fb64dade0c7ff2b
|
7
|
-
data.tar.gz: 6877c4151371466b579ad644866b96a88cfd24acc416583cf47ff45ad173f90de629f677b655e5d2f20594bf91f0bb68642ca706b385ebb58138cc77b02b4359
|
Binary file
|
@@ -1,33 +0,0 @@
|
|
1
|
-
# Pact best practices
|
2
|
-
|
3
|
-
## In your consumer project
|
4
|
-
|
5
|
-
#### Publish your pacts as artifacts on your CI machine or use a [Pact Broker](https://github.com/bethesque/pact_broker)
|
6
|
-
|
7
|
-
Either of these techniques makes the pact available via URL, which your provider build can then use when it runs pact:verify. This means your provider will always be verified against the latest pact from your consumer.
|
8
|
-
|
9
|
-
#### Ensure all calls to the provider go through your provider client class
|
10
|
-
|
11
|
-
Do not hand create any HTTP requests in your consumer app or specs. Testing through your provider client class gives you the assurance that your consumer app will be creating exactly the HTTP requests that you think it should.
|
12
|
-
|
13
|
-
#### Use factories to create your expected models
|
14
|
-
|
15
|
-
Sure, you've checked that your client deserialises the HTTP response into the object you expect, but then you need to make sure in your other tests where you stub your client that you're stubbing it with a valid object. The best way to do this is to use factories for all your tests.
|
16
|
-
|
17
|
-
## In your provider project
|
18
|
-
|
19
|
-
#### Retrieve pacts from the [Pact Broker](https://github.com/bethesque/pact_broker) or use the pact artifact published by your consumer's CI build
|
20
|
-
|
21
|
-
Configure the pact_uri in the Pact.service_provider block with the pact URL of your last successful build, whether that's from the pact broker, or your CI build. This way you're only verifying green builds. No point verifying a broken one.
|
22
|
-
|
23
|
-
#### Add pact:verify to your default rake task
|
24
|
-
|
25
|
-
It should run with all your other tests. If an integration is broken, you want to know about it *before* you check in.
|
26
|
-
|
27
|
-
#### In pact:verify on the provider, only stub layers beneath where contents of the request body are extracted
|
28
|
-
|
29
|
-
If you don't _have_ to stub anything in the provider when running pact:verify, then don't. If you do need to stub something, make sure that you only stub the code that gets executed _after_ the contents of the request body have been extracted and/or validated, otherwise, there is no verification that what is included in the body of a request matches what is actually expected.
|
30
|
-
|
31
|
-
#### Stub calls to downstream systems
|
32
|
-
|
33
|
-
Consider making a separate pact with the downstream system and using shared fixtures.
|
@@ -1,22 +0,0 @@
|
|
1
|
-
# Development Workflow
|
2
|
-
|
3
|
-
Using consumer driven contracts enables you to reverse the "normal" order of development, allowing you to build your consumer, in its entirety if need be, before you build your provider.
|
4
|
-
|
5
|
-
The development process will be different for every organisation, but this is one that has worked for the pact authors.
|
6
|
-
|
7
|
-
## Initial development
|
8
|
-
1. Write consumer tests with pact
|
9
|
-
1. Implement consumer
|
10
|
-
1. Add `pact:publish` task to consumer build or publish pact as CI artifact
|
11
|
-
1. Create provider project
|
12
|
-
1. Configure pact:verify task to point to latest published pact
|
13
|
-
1. Implement provider until `pact:verify` passes
|
14
|
-
|
15
|
-
## New features
|
16
|
-
|
17
|
-
1. Add new feature, with pact specs, to consumer project on a new feature branch.
|
18
|
-
1. In the provider project, use `rake pact:verify:at[/path/to/pact/on/branch]` to verify the new pact.
|
19
|
-
1. Commit/release new provider feature.
|
20
|
-
1. Merge consumer branch into main development branch.
|
21
|
-
|
22
|
-
This may seem complex, but it is actually sufacing the underlying reality, that you cannot add new functionality to the consumer before it can be supported by the provider, and that the functionality that the provider supports should still be driven by the needs of the consumer.
|
data/documentation/faq.md
DELETED
@@ -1,81 +0,0 @@
|
|
1
|
-
# Frequently asked questions
|
2
|
-
|
3
|
-
### How does Pact differ from VCR?
|
4
|
-
|
5
|
-
Pact is like VCR in reverse. VCR records actual provider behaviour, and verifies that the consumer behaves as expected. Pact records consumer behaviour, and verifies that the provider behaves as expected. The advantages Pact provides are:
|
6
|
-
|
7
|
-
* The ability to develop the consumer (eg. a Javascript rich client UI) before the provider (eg. the JSON backend API).
|
8
|
-
* The ability to drive out the requirements for your provider first, meaning you implement exactly and only what you need in the provider.
|
9
|
-
* Well documented use cases ("Given ... a request for ... will return ...") that show exactly how a provider is being used.
|
10
|
-
* The ability to see exactly which fields each consumer is interested in, allowing unused fields to be removed, and new fields to be added in the provider API without impacting a consumer.
|
11
|
-
* The ability to immediately see which consumers will be broken if a change is made to the provider API.
|
12
|
-
* When using the [Pact Broker](https://github.com/bethesque/pact_broker), the ability to map the relationships between your services.
|
13
|
-
|
14
|
-
### How does Pact differ from Webmock?
|
15
|
-
|
16
|
-
Unlike Webmock:
|
17
|
-
|
18
|
-
* Pact provides verification that the responses that have been stubbed are actually the responses that will be returned in the given conditions.
|
19
|
-
* Pact runs a mock server in an actual process, rather than intercepting requests within the Ruby code, allowing Javascript rich UI clients to be tested in a browser.
|
20
|
-
|
21
|
-
### How can I handle versioning?
|
22
|
-
|
23
|
-
Consumer driven contracts to some extent allows you to do away with versioning. As long as all your contract tests pass, you should be able to deploy changes without versioning the API. If you need to make a breaking change to a provider, you can do it in a multiple step process - add the new fields/endpoints to the provider and deploy. Update the consumers to use the new fields/endpoints, then deploy. Remove the old fields/endpoints from the provider and deploy. At each step of the process, all the contract tests remain green.
|
24
|
-
|
25
|
-
Using a [Pact Broker](https://github.com/bethesque/pact_broker), you can tag the production version of a pact when you make a release of a consumer. Then, any changes that you make to the provider can be checked agains the production version of the pact, as well as the latest version, to ensure backward compatiblity.
|
26
|
-
|
27
|
-
If you need to support multiple versions of the provider API concurrently, then you will probably be specifying which version your consumer uses by setting a header, or using a different URL component. As these are actually different requests, the interactions can be verified in the same pact without any problems.
|
28
|
-
|
29
|
-
### How can I verify a pact against a non-ruby provider?
|
30
|
-
|
31
|
-
You can verify a pact against any running server, regardless of language, using [pact-provider-proxy](https://github.com/bethesque/pact-provider-proxy).
|
32
|
-
|
33
|
-
There is also a JVM version of pact under development. Have a look at [pact-jvm](https://github.com/DiUS/pact-jvm), the that contains the equivalent of pact/consumer and pact/provider.
|
34
|
-
|
35
|
-
### How can I create a pact for a consumer that is not ruby or on the JVM?
|
36
|
-
|
37
|
-
Become famous, and write a pact-consumer library yourself! Then let us know about it so we can put a link to it in the documentation.
|
38
|
-
|
39
|
-
### How can I specify hooks to be executed before/after all examples for pact:verify?
|
40
|
-
|
41
|
-
Use the set_up and tear_down hooks in the provider state definition:
|
42
|
-
|
43
|
-
```ruby
|
44
|
-
|
45
|
-
Pact.provider_states_for "Some Consumer" do
|
46
|
-
|
47
|
-
set_up do
|
48
|
-
# Set up code here
|
49
|
-
end
|
50
|
-
|
51
|
-
tear_down do
|
52
|
-
# tear down code here
|
53
|
-
end
|
54
|
-
|
55
|
-
end
|
56
|
-
```
|
57
|
-
|
58
|
-
See https://www.relishapp.com/rspec/rspec-core/docs/hooks/filters for more information.
|
59
|
-
|
60
|
-
### How can I run my consumer UI during my consumer specs so I can execute the tests using a browser?
|
61
|
-
|
62
|
-
Eg. for Capybara tests
|
63
|
-
|
64
|
-
```ruby
|
65
|
-
Pact.service_consumer "My Consumer" do
|
66
|
-
app <your rack app here>
|
67
|
-
port 4321
|
68
|
-
end
|
69
|
-
```
|
70
|
-
|
71
|
-
### Should the database or any other part of the provider be stubbed?
|
72
|
-
|
73
|
-
The pact authors' experience with using pacts to test microservices has been that using the set_up hooks to populate the database, and running pact:verify with all the real provider code has worked very well, and gives us full confidence that the end to end scenario will work in the deployed code.
|
74
|
-
|
75
|
-
However, if you have a large and complex provider, you might decide to stub some of your application code. You will definitly need to stub calls to downstream systems or to set up error scenarios. Make sure, if you stub, that you don't stub the code that actually parses the request and pulls the expected data out, because otherwise the consumer could be sending absolute rubbish, and the pact:verify won't fail because that code won't get executed. If the validation happens when you insert a record into the datasource, either don't stub anything, or rethink your validation code.
|
76
|
-
|
77
|
-
### Why are the pacts generated and not static?
|
78
|
-
|
79
|
-
* Maintainability: Pact is "contract by example", and the examples may involve large quantities of JSON. Maintaining the JSON files by hand would be both time consuming and error prone. By dynamically creating the pacts, you have the option to keep your expectations in fixture files, or to generate them from your domain (the recommended approach, as it ensures your domain objects and their JSON representations in the pacts can never get out of sync).
|
80
|
-
|
81
|
-
* Provider states: Dynamically setting expectations on the mock server allows the use of provider states, meaning you can make the same request in different tests, with different expected responses. This allows you to properly test all the code paths in your consumer (eg. with different response codes, or different states of the resource). If all the interactions were loaded at start up from a static file, the mock server wouldn't know which response to return. See this [gist](https://gist.github.com/bethesque/7fa8947c107f92ace9a4) as an example.
|
@@ -1,178 +0,0 @@
|
|
1
|
-
# Provider States
|
2
|
-
|
3
|
-
Provider states allow you to set up data on the provider before the interaction is run, so that it can make a response that matches what the consumer expects. It also allows the consumer to make the same request with different expected responses.
|
4
|
-
|
5
|
-
Keep in mind that a provider state is all about the state of the *provider* (eg. what data is there, how it is going to handle a given response), not about the state of the consumer, or about what is in the request.
|
6
|
-
|
7
|
-
The text in the provider state should make sense when you read it as follows (this is how the autogenerated documentation reads):
|
8
|
-
|
9
|
-
|
10
|
-
Given **an alligator with the name Mary exists** \*
|
11
|
-
Upon receiving **a request to retrieve an alligator by name** \*\* from Some Consumer
|
12
|
-
With {"method" : "get", "path" : "/alligators/Mary" }
|
13
|
-
Some Provider will respond with { "status" : 200, ...}
|
14
|
-
|
15
|
-
\* This is the provider state
|
16
|
-
\*\* This is the request description
|
17
|
-
|
18
|
-
### Consumer codebase
|
19
|
-
|
20
|
-
For example, some code that creates a pact in a consumer project might look like this:
|
21
|
-
|
22
|
-
```ruby
|
23
|
-
describe MyServiceProviderClient do
|
24
|
-
|
25
|
-
subject { MyServiceProviderClient.new }
|
26
|
-
|
27
|
-
describe "get_something" do
|
28
|
-
context "when a thing exists" do
|
29
|
-
before do
|
30
|
-
my_service.given("a thing exists").
|
31
|
-
upon_receiving("a request for a thing").with(method: 'get', path: '/thing').
|
32
|
-
will_respond_with(status: 200,
|
33
|
-
headers: { 'Content-Type' => 'application/json' },
|
34
|
-
body: { name: 'A small something'} )
|
35
|
-
end
|
36
|
-
|
37
|
-
it "returns a thing" do
|
38
|
-
expect(subject.get_something).to eq(SomethingModel.new('A small something'))
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
context "when a thing does not exist" do
|
43
|
-
before do
|
44
|
-
my_service.given("a thing does not exist").
|
45
|
-
upon_receiving("a request for a thing").with(method: 'get', path: '/thing').
|
46
|
-
will_respond_with(status: 404)
|
47
|
-
end
|
48
|
-
|
49
|
-
it "returns nil" do
|
50
|
-
expect(subject.get_something).to be_nil
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
```
|
56
|
-
|
57
|
-
### Provider codebase
|
58
|
-
|
59
|
-
To define service provider states that create the right data for the provider states described above, write the following in the service provider project. (The consumer name here must match the name of the consumer configured in your consumer project for it to correctly find these provider states.)
|
60
|
-
|
61
|
-
```ruby
|
62
|
-
# In /spec/service_consumers/provider_states_for_my_service_consumer.rb
|
63
|
-
|
64
|
-
Pact.provider_states_for 'My Service Consumer' do
|
65
|
-
|
66
|
-
provider_state "a thing exists" do
|
67
|
-
set_up do
|
68
|
-
# Create a thing here using your framework of choice
|
69
|
-
# eg. Sequel.sqlite[:somethings].insert(name: "A small something")
|
70
|
-
end
|
71
|
-
|
72
|
-
tear_down do
|
73
|
-
# Any tear down steps to clean up your code
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
provider_state "a thing does not exist" do
|
78
|
-
no_op # If there's nothing to do because the state name is more for documentation purposes,
|
79
|
-
# you can use no_op to imply this.
|
80
|
-
end
|
81
|
-
|
82
|
-
end
|
83
|
-
```
|
84
|
-
Require your provider states file in the `pact_helper.rb`
|
85
|
-
|
86
|
-
```ruby
|
87
|
-
# In /spec/service_consumers/pact_helper.rb
|
88
|
-
|
89
|
-
require './spec/service_consumers/provider_states_for_my_service_consumer.rb'
|
90
|
-
```
|
91
|
-
|
92
|
-
### Base state
|
93
|
-
|
94
|
-
To define code that should run before/after each interaction for a given consumer, regardless of whether a provider state is specified or not, define set_up/tear_down blocks with no wrapping provider_state.
|
95
|
-
|
96
|
-
```ruby
|
97
|
-
Pact.provider_states_for 'My Service Consumer' do
|
98
|
-
|
99
|
-
set_up do
|
100
|
-
# This will run before the set_up for provider state specified for the interaction.
|
101
|
-
# eg. create API user, set the expected basic auth details
|
102
|
-
end
|
103
|
-
|
104
|
-
tear_down do
|
105
|
-
# ...
|
106
|
-
# This will run after the tear_down for the specified provider state.
|
107
|
-
end
|
108
|
-
end
|
109
|
-
```
|
110
|
-
|
111
|
-
### Global state
|
112
|
-
|
113
|
-
Global state will be set up before consumer specific base state. Avoid using the global set up for creating data as it will make your tests brittle when more than one consumer exists.
|
114
|
-
|
115
|
-
```ruby
|
116
|
-
Pact.set_up do
|
117
|
-
# eg. start database cleaner transaction
|
118
|
-
end
|
119
|
-
|
120
|
-
Pact.tear_down do
|
121
|
-
# eg. clean database
|
122
|
-
end
|
123
|
-
```
|
124
|
-
|
125
|
-
### Testing error responses
|
126
|
-
|
127
|
-
It is important to test how your client will handle error responses.
|
128
|
-
|
129
|
-
```ruby
|
130
|
-
# Consumer codebase
|
131
|
-
|
132
|
-
describe MyServiceProviderClient do
|
133
|
-
|
134
|
-
subject { MyServiceProviderClient.new }
|
135
|
-
|
136
|
-
describe "get_something" do
|
137
|
-
|
138
|
-
context "when an error occurs retrieving a thing" do
|
139
|
-
before do
|
140
|
-
my_service.given("an error occurs while retrieving a thing").
|
141
|
-
upon_receiving("a request for a thing").with(method: 'get', path: '/thing').
|
142
|
-
will_respond_with(
|
143
|
-
status: 500,
|
144
|
-
headers: { 'Content-Type' => 'application/json' },
|
145
|
-
body: { message: "An error occurred!" } )
|
146
|
-
end
|
147
|
-
|
148
|
-
it "raises an error" do
|
149
|
-
expect{ subject.get_something }.to raise_error /An error occurred!/
|
150
|
-
end
|
151
|
-
|
152
|
-
end
|
153
|
-
end
|
154
|
-
end
|
155
|
-
```
|
156
|
-
|
157
|
-
```ruby
|
158
|
-
# Provider codebase
|
159
|
-
|
160
|
-
Pact.provider_states_for 'My Service Consumer' do
|
161
|
-
provider_state "an error occurs while retrieving a thing" do
|
162
|
-
set_up do
|
163
|
-
# Stubbing is ususally the easiest way to generate an error with predictable error text.
|
164
|
-
ThingRepository.stub(:find).and_raise("An error occurred!")
|
165
|
-
end
|
166
|
-
end
|
167
|
-
end
|
168
|
-
```
|
169
|
-
|
170
|
-
### Including modules for use in set_up and tear_down
|
171
|
-
|
172
|
-
Any modules included this way will be available in the set_up and tear_down blocks. One common use of this to include factory methods for setting up data so that the provider states file doesn't get too bloated.
|
173
|
-
|
174
|
-
```ruby
|
175
|
-
Pact.configure do | config |
|
176
|
-
config.include MyTestHelperMethods
|
177
|
-
end
|
178
|
-
```
|
data/documentation/raq.md
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
# Rarely asked questions
|
2
|
-
|
3
|
-
### How can I run a standalone mock server?
|
4
|
-
|
5
|
-
By default, a mock service will be started automatically by the pact gem when running the consumer tests. A standalone mock service can be run locally and is useful for debugging purposes.
|
6
|
-
|
7
|
-
```ruby
|
8
|
-
Pact.service_consumer "My Service Consumer" do
|
9
|
-
has_pact_with "My Service Provider" do
|
10
|
-
mock_service :my_service_provider do
|
11
|
-
port <port-num>
|
12
|
-
standalone true #Tell the pact gem not to automatically start the mock service
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
```
|
17
|
-
$ bundle exec pact service -p <port-num>
|
18
|
-
|
19
|
-
The service prints messages it recieves to stdout which can be really useful
|
20
|
-
when diagnosing issues with pacts.
|
21
|
-
|
22
|
-
### Doesn't this break HAL?
|
23
|
-
|
24
|
-
Yes.
|
25
|
-
|
26
|
-
### How can I specify multiple headers with the same name?
|
27
|
-
|
28
|
-
RFC 2616 states that two headers with the same name can interpreted as a single header with two comma-separated values. This is the safest way to specify multiple headers with the same name, as Rack will only pass the last value through when they are defined separately (see https://github.com/rack/rack/issues/436).
|
29
|
-
|
30
|
-
```ruby
|
31
|
-
my_service_provider.
|
32
|
-
.given("it is RFC 2616 compliant")
|
33
|
-
.upon_receiving("a request with a header with commas separated values")
|
34
|
-
.with( method: :get, path: '/', headers: {'X-Request-Multival' => "A, B"} )
|
35
|
-
.will_respond_with(
|
36
|
-
status: 200, headers: {'X-Response-Multival' => "C, D"}
|
37
|
-
)
|
38
|
-
|
39
|
-
```
|
@@ -1,25 +0,0 @@
|
|
1
|
-
# Terminology
|
2
|
-
|
3
|
-
### Service Consumer
|
4
|
-
|
5
|
-
A component that initiates a HTTP request to another component (the service provider). Note that this does not depend on the way the data flows - whether it is a GET or a PUT/POST/PATCH, the consumer is the initiator of the HTTP request.
|
6
|
-
|
7
|
-
### Service Provider
|
8
|
-
|
9
|
-
A server that responds to an HTTP request from another component (the service consumer).
|
10
|
-
|
11
|
-
### Mock Service Provider
|
12
|
-
|
13
|
-
Used by tests in the consumer project to mock out the actual service provider, meaning that integration-like tests can be run without requiring the actual service provider to be available.
|
14
|
-
|
15
|
-
### Pact file
|
16
|
-
|
17
|
-
A file containing the JSON serialised requests and responses that were defined in the consumer tests.
|
18
|
-
|
19
|
-
### Pact verification
|
20
|
-
|
21
|
-
To verify a pact, the requests contained in a pact file are replayed against the provider code, and the responses returned are checked to ensure they match those expected in the pact file.
|
22
|
-
|
23
|
-
### Provider state
|
24
|
-
|
25
|
-
A name describing a "state" (like a fixture) that the provider should be in when a given request is replayed against it. Eg. "there is an alligator called Mary" or "there are no alligators". A provider state name is specified when writing the consumer specs, then, when the pact verification is set up in the provider, the same name will be used to identify the set up code block that should be run before the request is executed.
|
@@ -1,106 +0,0 @@
|
|
1
|
-
# Verifying pacts
|
2
|
-
|
3
|
-
"Verifying a pact" is the second step of the Pact testing process. Each request in the pact file is replayed against
|
4
|
-
the provider, and the response that is returned is compared with the expected response in the pact file, and if the two
|
5
|
-
match, then we know the consumer and provider are compatible.
|
6
|
-
|
7
|
-
To verify a pact, we must:
|
8
|
-
|
9
|
-
1. Configure the location of the pact to be verified. This can be a HTTP URL, or a local file system path.
|
10
|
-
|
11
|
-
2. Set up the data for the [provider states](/documentation/provider-states.md).
|
12
|
-
|
13
|
-
## Using rake pact:verify
|
14
|
-
|
15
|
-
Using the pact:verify task is the default way to verify pacts. It is made available by requiring `'pact/tasks'` in your Rakefile.
|
16
|
-
|
17
|
-
```ruby
|
18
|
-
# In Rakefile
|
19
|
-
require 'pact/tasks'
|
20
|
-
```
|
21
|
-
|
22
|
-
The pacts that will be verified by the pact:verify task are configured in the pact_helper.rb file in your provider codebase.
|
23
|
-
The file must be called pact_helper.rb, however there is some flexibility in where it can be stored.
|
24
|
-
The recommended place is `spec/service_consumers/pact_helper.rb`.
|
25
|
-
|
26
|
-
To ensure that the latest version of the consumer pact is used each time, it is recommended that you either use a [Pact Broker](https://github.com/bethesque/pact_broker)
|
27
|
-
or that you publish the pacts of a successful consumer build as artefacts in your CI system.
|
28
|
-
|
29
|
-
```ruby
|
30
|
-
# In specs/service_consumers/pact_helper.rb
|
31
|
-
|
32
|
-
require 'pact/provider/rspec'
|
33
|
-
|
34
|
-
Pact.service_provider "My Service Provider" do
|
35
|
-
|
36
|
-
app { MyApp.new } # Optional, loads app from config.ru by default
|
37
|
-
|
38
|
-
honours_pact_with 'My Service Consumer' do
|
39
|
-
|
40
|
-
# This example points to a local file, however, on a real project with a continuous
|
41
|
-
# integration box, you would publish your pacts as artifacts,
|
42
|
-
# and point the pact_uri to the pact published by the last successful build.
|
43
|
-
|
44
|
-
pact_uri '../path-to-your-consumer-project/specs/pacts/my_consumer-my_provider.json'
|
45
|
-
end
|
46
|
-
|
47
|
-
# This block is repeated for every pact that this provider should be verified against.
|
48
|
-
honours_pact_with 'Some other Service Consumer' do
|
49
|
-
...
|
50
|
-
end
|
51
|
-
|
52
|
-
end
|
53
|
-
```
|
54
|
-
|
55
|
-
## Using rake pact:verify:at
|
56
|
-
|
57
|
-
You can also verify a pact at any arbitrary local or remote URL using the `pact:verify:at` task.
|
58
|
-
This is useful when you are writing the consumer and provider concurrently, and wish to
|
59
|
-
|
60
|
-
$ rake pact:verify:at[../path-to-your-consumer-project/specs/pacts/my_consumer-my_provider.json]
|
61
|
-
$ rake pact:verify:at[http://build-box/MyConsumerBuild/latestSuccessful/artifacts/my_consumer-my_provider.json]
|
62
|
-
|
63
|
-
|
64
|
-
## Using a custom pact:verify task
|
65
|
-
|
66
|
-
To make a shortcut task for verifying a pact an arbitrary URL that you do not want to verify as part of your normal pact:verify task,
|
67
|
-
(eg. when you are developing the consumer and provider side by side, and want a shorter feedback cycle than can be provided by
|
68
|
-
by your CI box) add the following to your Rakefile. The pact.uri may be a local file system path or a remote URL.
|
69
|
-
|
70
|
-
```ruby
|
71
|
-
# In Rakefile or /tasks/pact.rake
|
72
|
-
|
73
|
-
# This creates a rake task that can be executed by running
|
74
|
-
# $ rake pact:verify:dev
|
75
|
-
|
76
|
-
Pact::VerificationTask.new(:dev) do | task |
|
77
|
-
task.uri '../path-to-your-consumer-project/specs/pacts/my_consumer-my_provider.json'
|
78
|
-
end
|
79
|
-
```
|
80
|
-
|
81
|
-
## Running one pact at a time
|
82
|
-
|
83
|
-
At some stage, you'll want to be able to run your specs one at a time while you implement each feature. At the bottom of the failed pact:verify output you will see the commands to rerun each failed interaction individually. A command to run just one interaction will look like this:
|
84
|
-
|
85
|
-
$ rake pact:verify PACT_DESCRIPTION="a request for something" PACT_PROVIDER_STATE="something exists"
|
86
|
-
|
87
|
-
## Configuring RSpec
|
88
|
-
|
89
|
-
Pact uses dynamically created RSpec specs to verify pacts. If you want to modify the behaviour of the underlying RSpec execution, you can:
|
90
|
-
|
91
|
-
1. Set `task.rspec_opts` in your custom rake VerificationTask, the same way you would with a normal RSpec rake task declaration.
|
92
|
-
1. Configure RSpec in the pact_helper using the normal `RSpec.configure` code.
|
93
|
-
|
94
|
-
For future proofing though, try to use the provider state set_up/tear_down blocks where you can, because we may swap out RSpec for custom verification code in the future.
|
95
|
-
|
96
|
-
## Pact Helper location
|
97
|
-
|
98
|
-
The search paths for the pact_helper are:
|
99
|
-
|
100
|
-
```ruby
|
101
|
-
[
|
102
|
-
"spec/**/*service*consumer*/pact_helper.rb",
|
103
|
-
"spec/**/*consumer*/pact_helper.rb",
|
104
|
-
"spec/**/pact_helper.rb",
|
105
|
-
"**/pact_helper.rb"]
|
106
|
-
```
|