pact 1.0.38 → 1.0.39

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,13 @@
1
1
  Do this to generate your change history
2
2
 
3
- git log --date=relative --pretty=format:' * %h - %s (%an, %ad)'
3
+ git log --pretty=format:' * %h - %s (%an, %ad)'
4
+
5
+ ### 1.0.39 (8 April 2014)
6
+
7
+ * a034ab6 - Oh ActiveSupport, why??? Fixing to_json for difference indicators (bethesque, Mon Apr 7 17:26:10 2014 +1000)
8
+ * 1c7fa0d - Update faq.md (bethesque, Thu Apr 3 09:58:02 2014 +1100)
9
+ * 8cf5b57 - Update README.md (bethesque, Thu Mar 27 13:38:13 2014 +1100)
10
+ * 1c5fde9 - Preloading app before suite in pact:verify Ensures consistent behaviour between the first before/after each hooks (bethesque, Thu Mar 27 10:0
4
11
 
5
12
  ### 1.0.38 (24 March 2014)
6
13
 
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pact (1.0.38)
4
+ pact (1.0.39)
5
5
  awesome_print (~> 1.1)
6
6
  colored
7
7
  find_a_port (~> 1.0.1)
@@ -58,7 +58,7 @@ GEM
58
58
  rspec-mocks (2.14.3)
59
59
  safe_yaml (0.9.5)
60
60
  slop (3.4.6)
61
- thor (0.18.1)
61
+ thor (0.19.1)
62
62
  thread_safe (0.1.3)
63
63
  atomic
64
64
  tzinfo (0.3.38)
data/README.md CHANGED
@@ -28,14 +28,15 @@ Travis CI Status: [![travis-ci.org Build Status](https://travis-ci.org/realestat
28
28
  ## Why is developing and testing with pacts better than using integration tests?
29
29
 
30
30
  * Faster execution.
31
- * No need to manage starting and stopping multiple processes.
32
31
  * Reliable responses from mock service provider reduce likelihood of flakey tests.
33
32
  * Only one component is being tested at a time, making the causes of test failures easier to identify.
34
33
  * Design of service provider is improved by considering first how the data is actually going to be used, rather than how it is most easily retrieved and serialised.
34
+ * No need to manage starting, stopping and fixture set up for multiple applications during a test run.
35
35
 
36
- ## Google users group
36
+ ## Contact
37
37
 
38
- https://groups.google.com/forum/#!forum/pact-support
38
+ * Twitter: [@pact_up](https://twitter.com/pact_up)
39
+ * Google users group: https://groups.google.com/forum/#!forum/pact-support
39
40
 
40
41
  ## Installation
41
42
 
@@ -172,13 +173,22 @@ Create your API class using the framework of your choice (e.g. Sinatra, Grape) -
172
173
 
173
174
  #### 2. Tell your provider that it needs to honour the pact file you made earlier
174
175
 
176
+ Require "pact/tasks" in your Rakefile.
177
+
178
+ ```ruby
179
+ # In Rakefile
180
+
181
+ require 'pact/tasks'
182
+ ```
183
+
175
184
  Create a `pact_helper.rb` in your service provider project. The file must be called pact_helper.rb, however there is some flexibility in where it can be stored. The recommended place is `specs/service_consumers/pact_helper.rb`.
176
185
 
177
186
  ```ruby
187
+ # In specs/service_consumers/pact_helper.rb
188
+
178
189
  require 'pact/provider/rspec'
179
- # If you wish to use the same spec_helper file as your unit tests, require it here.
190
+ # If you wish to use the same spec_helper file as your unit tests, require it here, but remember that the RSpec
180
191
  # Otherwise, you can set up a separate RSpec configuration in this file just for pact:verify.
181
- require './spec_helper'
182
192
 
183
193
  Pact.service_provider "My Service Provider" do
184
194
 
@@ -195,13 +205,7 @@ Pact.service_provider "My Service Provider" do
195
205
  end
196
206
 
197
207
  ```
198
- Require "pact/tasks" in your Rakefile. If the pact gem is in the test/development section of your Gemfile, you may want to put an env check around this so it doesn't load the pact tasks in prod.
199
208
 
200
- ```ruby
201
- # In Rakefile
202
-
203
- require 'pact/tasks'
204
- ```
205
209
 
206
210
  #### 3. Run your failing specs
207
211
 
@@ -209,11 +213,13 @@ require 'pact/tasks'
209
213
 
210
214
  Congratulations! You now have a failing spec to develop against.
211
215
 
212
- #### 4. Implement your service provider
216
+ At this 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:
213
217
 
214
- At this stage, you'll probably want to be able to run your specs one at a time while you implement. Define the environment variables PACT_DESCRIPTION and/or PACT_PROVIDER_STATE as so:
218
+ $ rake pact:verify PACT_DESCRIPTION="a request for something" PACT_PROVIDER_STATE="something exists"
215
219
 
216
- $ PACT_DESCRIPTION="a request for something" PACT_PROVIDER_STATE="something exists" rake pact:verify
220
+ #### 4. Implement enough to make your first interaction spec pass
221
+
222
+ Rinse and repeat.
217
223
 
218
224
  #### 5. Keep going til you're green
219
225
 
@@ -249,8 +255,6 @@ my_service.
249
255
  will_respond_with(status: 500, :body => {message: "An error occurred"}, :headers => { 'Content-Type' => 'application/json'} )
250
256
  ```
251
257
 
252
-
253
-
254
258
  To define service provider states that create the right data for "a thing exists" and "a thing does not exist", 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.)
255
259
 
256
260
 
@@ -274,6 +278,7 @@ Pact.provider_states_for 'My Service Consumer' do
274
278
 
275
279
  provider_state "an error occurs while retrieving a thing" do
276
280
  set_up do
281
+ # Stubbing is ususally the easiest way to generate an error with predictable error text.
277
282
  ThingRepository.stub(:find).and_raise("An error occurred!")
278
283
  end
279
284
  end
@@ -377,13 +382,13 @@ Configure the pact_uri in the Pact.service_provider block with the pact artifact
377
382
 
378
383
  It should run with all your other tests. If an integration is broken, you want to know about it *before* you check in.
379
384
 
380
- #### Stub calls to downstream systems
385
+ #### In pact:verify on the provider, only stub layers beneath where contents of the request body are extracted
381
386
 
382
- Consider making a separate pact with the downstream system and using shared fixtures.
387
+ 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.
383
388
 
384
- #### Consider carefully whether to use the real database or stub calls
389
+ #### Stub calls to downstream systems
385
390
 
386
- You may choose not stub your database calls for pact:verify. This can be a good time for you to test your database integration if you have a simple application, however, for a complex one, you might want to carefully choose a point at which to stub calls.
391
+ Consider making a separate pact with the downstream system and using shared fixtures.
387
392
 
388
393
  ## Gotchas
389
394
 
@@ -1,6 +1,24 @@
1
1
  # Frequently asked questions
2
2
 
3
- ## How can I verify a pact against a non-ruby provider?
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 verify a pact against a non-ruby provider?
4
22
 
5
23
  You can verify a pact against any running server, regardless of language, using [pact-provider-proxy](https://github.com/bethesque/pact-provider-proxy).
6
24
 
@@ -12,13 +30,20 @@ Become famous, and write a pact-consumer library yourself! Then let us know abou
12
30
 
13
31
  ### How can I specify hooks to be executed before/after all examples for pact:verify?
14
32
 
15
- The pact:verify RSpec examples have the metadata `{:pact => :verify}` defined. You can add RSpec hooks using a filter as shown here:
33
+ Use the set_up and tear_down hooks in the provider state definition:
16
34
 
17
35
  ```ruby
18
- RSpec.configure do | config |
19
- config.before :each, :pact => :verify do
20
- # Your code here
36
+
37
+ Pact.provider_states_for "Some Consumer" do
38
+
39
+ set_up do
40
+ # Set up code here
41
+ end
42
+
43
+ tear_down do
44
+ # tear down code here
21
45
  end
46
+
22
47
  end
23
48
  ```
24
49
 
@@ -41,5 +66,10 @@ This is a hotly debated issue.
41
66
 
42
67
  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.
43
68
 
44
- However, if you have a large and complex provider, you might decide to stub some of your application code. If you do, remember to add your own "verification" of your stubbed methods - write another test that will break if the behaviour of the stubbed methods changes.
69
+ 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. Make sure, when you stub, that you don't stub the code that actually parses the requests, because otherwise the consumer could be sending absolute rubbish, and you won't be checking it.
70
+
71
+ ### Why are the pacts generated and not hand coded?
72
+
73
+ * 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).
45
74
 
75
+ * Provider states: Dynamically setting expectations on the mock server allows the use of provider states, meaning you can make the same request more than once, with different expected responses, allowing you to properly test all your code paths.
@@ -43,5 +43,9 @@ module Pact
43
43
  end
44
44
  end
45
45
 
46
+ def remove_unicode json
47
+ json.gsub(/\\u([0-9A-Za-z]{4})/) {|s| [$1.to_i(16)].pack("U")}
48
+ end
49
+
46
50
  end
47
51
  end
@@ -0,0 +1,26 @@
1
+ require 'pact/consumer_contract/active_support_support'
2
+
3
+ module Pact
4
+ class DifferenceIndicator
5
+
6
+ include ActiveSupportSupport
7
+
8
+ def == other
9
+ other.is_a? self.class
10
+ end
11
+
12
+ def eql? other
13
+ self == other
14
+ end
15
+
16
+ def to_json options = {}
17
+ remove_unicode as_json.to_json(options)
18
+ end
19
+
20
+ def as_json options = {}
21
+ to_s
22
+ end
23
+
24
+ end
25
+
26
+ end
@@ -1,21 +1,12 @@
1
+ require 'pact/matchers/difference_indicator'
2
+
1
3
  module Pact
2
- class IndexNotFound
3
- def == other
4
- other.is_a? IndexNotFound
5
- end
4
+ class IndexNotFound < Pact::DifferenceIndicator
6
5
 
7
6
  def to_s
8
7
  "<index not found>"
9
8
  end
10
9
 
11
- def to_json options = {}
12
- as_json.to_json options
13
- end
14
-
15
- def as_json options = {}
16
- to_s
17
- end
18
-
19
10
  def empty?
20
11
  true
21
12
  end
@@ -1,21 +1,11 @@
1
- module Pact
2
- class UnexpectedIndex
1
+ require 'pact/matchers/difference_indicator'
3
2
 
4
- def == other
5
- other.is_a? UnexpectedIndex
6
- end
3
+ module Pact
4
+ class UnexpectedIndex < Pact::DifferenceIndicator
7
5
 
8
6
  def to_s
9
7
  '<index not to exist>'
10
8
  end
11
9
 
12
- def as_json options = {}
13
- to_s
14
- end
15
-
16
- def to_json opts = {}
17
- as_json.to_json options
18
- end
19
-
20
10
  end
21
11
  end
@@ -1,20 +1,11 @@
1
- module Pact
2
- class UnexpectedKey
1
+ require 'pact/matchers/difference_indicator'
3
2
 
4
- def == other
5
- other.is_a? UnexpectedKey
6
- end
3
+ module Pact
4
+ class UnexpectedKey < Pact::DifferenceIndicator
7
5
 
8
6
  def to_s
9
7
  '<key not to exist>'
10
8
  end
11
9
 
12
- def as_json options = {}
13
- to_s
14
- end
15
-
16
- def to_json opts = {}
17
- as_json.to_json options
18
- end
19
10
  end
20
11
  end
@@ -90,6 +90,14 @@ module Pact
90
90
  config.add_formatter Pact::Provider::RSpec::Formatter
91
91
  config.add_formatter Pact::Provider::RSpec::SilentJsonFormatter
92
92
 
93
+ config.before(:suite) do
94
+ # Preload app before suite so the classes loaded in memory are consistent for
95
+ # before :each and after :each hooks.
96
+ # Otherwise the app and all its dependencies are loaded between the first before :each
97
+ # and the first after :each, leading to inconsistent behaviour
98
+ # (eg. with database_cleaner transactions)
99
+ Pact.configuration.provider.app
100
+ end
93
101
  end
94
102
 
95
103
  def run_specs
@@ -1,25 +1,12 @@
1
- module Pact
2
- class KeyNotFound
3
- def == other
4
- other.is_a? KeyNotFound
5
- end
1
+ require 'pact/matchers/difference_indicator'
6
2
 
7
- def eql? other
8
- self == other
9
- end
3
+ module Pact
4
+ class KeyNotFound < Pact::DifferenceIndicator
10
5
 
11
6
  def to_s
12
7
  "<key not found>"
13
8
  end
14
9
 
15
- def as_json options={}
16
- to_s
17
- end
18
-
19
- def to_json options = {}
20
- as_json.to_json options
21
- end
22
-
23
10
  def empty?
24
11
  true
25
12
  end
@@ -1,3 +1,3 @@
1
1
  module Pact
2
- VERSION = "1.0.38"
2
+ VERSION = "1.0.39"
3
3
  end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+ require 'pact/matchers/index_not_found'
3
+
4
+ module Pact
5
+ describe IndexNotFound do
6
+
7
+ describe "#as_json" do
8
+ it "returns a string representation of the object" do
9
+ expect(subject.as_json).to eq subject.to_s
10
+ end
11
+ end
12
+
13
+ describe "#to_json" do
14
+ it "serialises the object to JSON" do
15
+ expect(subject.to_json).to eq "\"#{subject.to_s}\""
16
+ end
17
+ end
18
+
19
+ end
20
+
21
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+ require 'pact/matchers/unexpected_index'
3
+
4
+ module Pact
5
+ describe UnexpectedIndex do
6
+
7
+ describe "#as_json" do
8
+ it "returns a string representation of the object" do
9
+ expect(subject.as_json).to eq subject.to_s
10
+ end
11
+ end
12
+
13
+ describe "#to_json" do
14
+ it "serialises the object to JSON" do
15
+ expect(subject.to_json).to eq "\"#{subject.to_s}\""
16
+ end
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+ require 'pact/matchers/unexpected_key'
3
+
4
+ module Pact
5
+ describe UnexpectedKey do
6
+
7
+ describe "#as_json" do
8
+ it "returns a string representation of the object" do
9
+ expect(subject.as_json).to eq subject.to_s
10
+ end
11
+ end
12
+
13
+ describe "#to_json" do
14
+ it "serialises the object to JSON" do
15
+ expect(subject.to_json).to eq "\"#{subject.to_s}\""
16
+ end
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+ require 'pact/shared/key_not_found'
3
+
4
+ module Pact
5
+ describe KeyNotFound do
6
+
7
+ describe "#as_json" do
8
+ it "returns a string representation of the object" do
9
+ expect(subject.as_json).to eq subject.to_s
10
+ end
11
+ end
12
+
13
+ describe "#to_json" do
14
+ it "serialises the object to JSON" do
15
+ expect(subject.to_json).to eq "\"#{subject.to_s}\""
16
+ end
17
+ end
18
+
19
+ end
20
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pact
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.38
4
+ version: 1.0.39
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -13,7 +13,7 @@ authors:
13
13
  autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
- date: 2014-03-23 00:00:00.000000000 Z
16
+ date: 2014-04-07 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: randexp
@@ -361,6 +361,7 @@ files:
361
361
  - lib/pact/logging.rb
362
362
  - lib/pact/matchers.rb
363
363
  - lib/pact/matchers/diff_decorator.rb
364
+ - lib/pact/matchers/difference_indicator.rb
364
365
  - lib/pact/matchers/index_not_found.rb
365
366
  - lib/pact/matchers/matchers.rb
366
367
  - lib/pact/matchers/unexpected_index.rb
@@ -425,7 +426,10 @@ files:
425
426
  - spec/lib/pact/consumer_contract/interaction_spec.rb
426
427
  - spec/lib/pact/consumer_contract/request_spec.rb
427
428
  - spec/lib/pact/matchers/diff_decorator_spec.rb
429
+ - spec/lib/pact/matchers/index_not_found_spec.rb
428
430
  - spec/lib/pact/matchers/matchers_spec.rb
431
+ - spec/lib/pact/matchers/unexpected_index_spec.rb
432
+ - spec/lib/pact/matchers/unexpected_key_spec.rb
429
433
  - spec/lib/pact/provider/configuration_spec.rb
430
434
  - spec/lib/pact/provider/pact_helper_locator_spec.rb
431
435
  - spec/lib/pact/provider/pact_spec_runner_spec.rb
@@ -438,6 +442,7 @@ files:
438
442
  - spec/lib/pact/provider/world_spec.rb
439
443
  - spec/lib/pact/reification_spec.rb
440
444
  - spec/lib/pact/shared/dsl_spec.rb
445
+ - spec/lib/pact/shared/key_not_found_spec.rb
441
446
  - spec/lib/pact/something_like_spec.rb
442
447
  - spec/lib/pact/tasks/task_helper_spec.rb
443
448
  - spec/lib/pact/term_spec.rb
@@ -474,7 +479,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
474
479
  version: '0'
475
480
  segments:
476
481
  - 0
477
- hash: 1671674810832384181
482
+ hash: 780584884635013301
478
483
  required_rubygems_version: !ruby/object:Gem::Requirement
479
484
  none: false
480
485
  requirements:
@@ -483,7 +488,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
483
488
  version: '0'
484
489
  segments:
485
490
  - 0
486
- hash: 1671674810832384181
491
+ hash: 780584884635013301
487
492
  requirements: []
488
493
  rubyforge_project:
489
494
  rubygems_version: 1.8.23
@@ -518,7 +523,10 @@ test_files:
518
523
  - spec/lib/pact/consumer_contract/interaction_spec.rb
519
524
  - spec/lib/pact/consumer_contract/request_spec.rb
520
525
  - spec/lib/pact/matchers/diff_decorator_spec.rb
526
+ - spec/lib/pact/matchers/index_not_found_spec.rb
521
527
  - spec/lib/pact/matchers/matchers_spec.rb
528
+ - spec/lib/pact/matchers/unexpected_index_spec.rb
529
+ - spec/lib/pact/matchers/unexpected_key_spec.rb
522
530
  - spec/lib/pact/provider/configuration_spec.rb
523
531
  - spec/lib/pact/provider/pact_helper_locator_spec.rb
524
532
  - spec/lib/pact/provider/pact_spec_runner_spec.rb
@@ -531,6 +539,7 @@ test_files:
531
539
  - spec/lib/pact/provider/world_spec.rb
532
540
  - spec/lib/pact/reification_spec.rb
533
541
  - spec/lib/pact/shared/dsl_spec.rb
542
+ - spec/lib/pact/shared/key_not_found_spec.rb
534
543
  - spec/lib/pact/something_like_spec.rb
535
544
  - spec/lib/pact/tasks/task_helper_spec.rb
536
545
  - spec/lib/pact/term_spec.rb