pact 1.0.39 → 1.1.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/CHANGELOG.md +1 -17
  2. data/Gemfile.lock +2 -2
  3. data/README.md +18 -27
  4. data/bethtest.rb +30 -0
  5. data/documentation/faq.md +6 -36
  6. data/lib/pact/configuration.rb +0 -4
  7. data/lib/pact/consumer/consumer_contract_builder.rb +1 -1
  8. data/lib/pact/consumer_contract/active_support_support.rb +0 -4
  9. data/lib/pact/matchers/diff_decorator.rb +1 -1
  10. data/lib/pact/matchers/index_not_found.rb +12 -3
  11. data/lib/pact/matchers/matchers.rb +2 -1
  12. data/lib/pact/matchers/nested_json_diff_decorator.rb +48 -0
  13. data/lib/pact/matchers/plus_minus_diff_decorator.rb +92 -0
  14. data/lib/pact/matchers/unexpected_index.rb +13 -3
  15. data/lib/pact/matchers/unexpected_key.rb +12 -3
  16. data/lib/pact/provider/configuration.rb +31 -0
  17. data/lib/pact/provider/matchers.rb +4 -4
  18. data/lib/pact/provider/pact_spec_runner.rb +28 -35
  19. data/lib/pact/provider/print_missing_provider_states.rb +2 -2
  20. data/lib/pact/provider/rspec.rb +7 -16
  21. data/lib/pact/provider/world.rb +0 -6
  22. data/lib/pact/shared/key_not_found.rb +16 -3
  23. data/lib/pact/tasks/task_helper.rb +18 -15
  24. data/lib/pact/templates/provider_state.erb +1 -0
  25. data/lib/pact/version.rb +1 -1
  26. data/spec/lib/pact/matchers/matchers_spec.rb +9 -0
  27. data/spec/lib/pact/matchers/nested_json_diff_decorator_spec.rb +48 -0
  28. data/spec/lib/pact/matchers/plus_minus_diff_decorator_spec.rb +186 -0
  29. data/spec/lib/pact/provider/configuration_spec.rb +131 -102
  30. data/spec/lib/pact/verification_task_spec.rb +10 -0
  31. data/spec/support/pact_helper.rb +2 -5
  32. data/spec/support/stubbing.json +1 -1
  33. data/spec/support/test_app_fail.json +2 -29
  34. data/spec/support/test_app_pass.json +4 -4
  35. data/tasks/pact-test.rake +0 -8
  36. metadata +13 -22
  37. data/lib/pact/matchers/difference_indicator.rb +0 -26
  38. data/lib/pact/provider/rspec/formatter.rb +0 -63
  39. data/lib/pact/provider/rspec/silent_json_formatter.rb +0 -18
  40. data/spec/lib/pact/matchers/index_not_found_spec.rb +0 -21
  41. data/spec/lib/pact/matchers/unexpected_index_spec.rb +0 -20
  42. data/spec/lib/pact/matchers/unexpected_key_spec.rb +0 -20
  43. data/spec/lib/pact/shared/key_not_found_spec.rb +0 -20
  44. data/spec/lib/pact/tasks/task_helper_spec.rb +0 -80
@@ -1,22 +1,6 @@
1
1
  Do this to generate your change history
2
2
 
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
11
-
12
- ### 1.0.38 (24 March 2014)
13
-
14
- * 7fb2bc3 - Improved readability of pact:verify specs by removing pactfile name and request details from output (bethesque, 23 hours ago)
15
- * ff1de3c - Improving readability of error messages when pact:verify fails (bethesque, 23 hours ago)
16
- * 8a08abf - Removed the last RSpec private API usage. I think. (bethesque, 33 hours ago)
17
- * 6a0be58 - Reducing even more use of RSpec private APIs (bethesque, 33 hours ago)
18
- * e1fd51c - Reducing use of RSpec private APIs (bethesque, 34 hours ago)
19
- * 587cb90 - Replaced rspec 'commands to rerun failed examples' with Pact specific commands to rerun failed interactions (bethesque, 2 days ago)
3
+ git log --date=relative --pretty=format:' * %h - %s (%an, %ad)'
20
4
 
21
5
  ### 1.0.37 (19 March 2014)
22
6
 
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pact (1.0.39)
4
+ pact (1.1.0.rc1)
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.19.1)
61
+ thor (0.18.1)
62
62
  thread_safe (0.1.3)
63
63
  atomic
64
64
  tzinfo (0.3.38)
data/README.md CHANGED
@@ -28,15 +28,14 @@ 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.
31
32
  * Reliable responses from mock service provider reduce likelihood of flakey tests.
32
33
  * Only one component is being tested at a time, making the causes of test failures easier to identify.
33
34
  * 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
- ## Contact
36
+ ## Google users group
37
37
 
38
- * Twitter: [@pact_up](https://twitter.com/pact_up)
39
- * Google users group: https://groups.google.com/forum/#!forum/pact-support
38
+ https://groups.google.com/forum/#!forum/pact-support
40
39
 
41
40
  ## Installation
42
41
 
@@ -173,22 +172,13 @@ Create your API class using the framework of your choice (e.g. Sinatra, Grape) -
173
172
 
174
173
  #### 2. Tell your provider that it needs to honour the pact file you made earlier
175
174
 
176
- Require "pact/tasks" in your Rakefile.
177
-
178
- ```ruby
179
- # In Rakefile
180
-
181
- require 'pact/tasks'
182
- ```
183
-
184
175
  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`.
185
176
 
186
177
  ```ruby
187
- # In specs/service_consumers/pact_helper.rb
188
-
189
178
  require 'pact/provider/rspec'
190
- # If you wish to use the same spec_helper file as your unit tests, require it here, but remember that the RSpec
179
+ # If you wish to use the same spec_helper file as your unit tests, require it here.
191
180
  # Otherwise, you can set up a separate RSpec configuration in this file just for pact:verify.
181
+ require './spec_helper'
192
182
 
193
183
  Pact.service_provider "My Service Provider" do
194
184
 
@@ -205,7 +195,13 @@ Pact.service_provider "My Service Provider" do
205
195
  end
206
196
 
207
197
  ```
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.
208
199
 
200
+ ```ruby
201
+ # In Rakefile
202
+
203
+ require 'pact/tasks'
204
+ ```
209
205
 
210
206
  #### 3. Run your failing specs
211
207
 
@@ -213,13 +209,11 @@ end
213
209
 
214
210
  Congratulations! You now have a failing spec to develop against.
215
211
 
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:
212
+ #### 4. Implement your service provider
217
213
 
218
- $ rake pact:verify PACT_DESCRIPTION="a request for something" PACT_PROVIDER_STATE="something exists"
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:
219
215
 
220
- #### 4. Implement enough to make your first interaction spec pass
221
-
222
- Rinse and repeat.
216
+ $ PACT_DESCRIPTION="a request for something" PACT_PROVIDER_STATE="something exists" rake pact:verify
223
217
 
224
218
  #### 5. Keep going til you're green
225
219
 
@@ -255,6 +249,8 @@ my_service.
255
249
  will_respond_with(status: 500, :body => {message: "An error occurred"}, :headers => { 'Content-Type' => 'application/json'} )
256
250
  ```
257
251
 
252
+
253
+
258
254
  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.)
259
255
 
260
256
 
@@ -278,7 +274,6 @@ Pact.provider_states_for 'My Service Consumer' do
278
274
 
279
275
  provider_state "an error occurs while retrieving a thing" do
280
276
  set_up do
281
- # Stubbing is ususally the easiest way to generate an error with predictable error text.
282
277
  ThingRepository.stub(:find).and_raise("An error occurred!")
283
278
  end
284
279
  end
@@ -382,13 +377,9 @@ Configure the pact_uri in the Pact.service_provider block with the pact artifact
382
377
 
383
378
  It should run with all your other tests. If an integration is broken, you want to know about it *before* you check in.
384
379
 
385
- #### In pact:verify on the provider, only stub layers beneath where contents of the request body are extracted
386
-
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.
388
-
389
- #### Stub calls to downstream systems
380
+ #### Use the real database
390
381
 
391
- Consider making a separate pact with the downstream system and using shared fixtures.
382
+ Do not stub your database calls for pact:verify. This is the best time for you to test your database integration. If you stub your database calls, you are getting little more assurance that the real end-to-end will work than if you'd used a unit test. It's the appropriate time to incur the overhead of a database call.
392
383
 
393
384
  ## Gotchas
394
385
 
@@ -0,0 +1,30 @@
1
+ require 'colored'
2
+
3
+ puts "Key: #{'-'.red} expected, #{'+'.green} actual"
4
+
5
+ puts '{
6
+ "_embedded": {'
7
+ puts '- "events": [
8
+ - {
9
+ - "probability": "75",
10
+ - "timestamp": "2014-03-03T14:00:00+00:00",
11
+ - "_embedded": {
12
+ - "opportunity": {
13
+ - "displayAdvertising": true,
14
+ - "_links": {
15
+ - "self": {
16
+ - "href": "http://media-opportunities/opportunity-id-1"
17
+ - },
18
+ - "mediaProject": {
19
+ - "href": "http://media-projects/project-id-1"
20
+ - }
21
+ - }
22
+ - }
23
+ - }
24
+ - }
25
+ - ]'.red
26
+ puts '+ {
27
+ + }'.green
28
+ puts ' }
29
+ }'
30
+
@@ -1,24 +1,6 @@
1
1
  # Frequently asked questions
2
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 verify a pact against a non-ruby provider?
3
+ ## How can I verify a pact against a non-ruby provider?
22
4
 
23
5
  You can verify a pact against any running server, regardless of language, using [pact-provider-proxy](https://github.com/bethesque/pact-provider-proxy).
24
6
 
@@ -30,20 +12,13 @@ Become famous, and write a pact-consumer library yourself! Then let us know abou
30
12
 
31
13
  ### How can I specify hooks to be executed before/after all examples for pact:verify?
32
14
 
33
- Use the set_up and tear_down hooks in the provider state definition:
15
+ The pact:verify RSpec examples have the metadata `{:pact => :verify}` defined. You can add RSpec hooks using a filter as shown here:
34
16
 
35
17
  ```ruby
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
18
+ RSpec.configure do | config |
19
+ config.before :each, :pact => :verify do
20
+ # Your code here
45
21
  end
46
-
47
22
  end
48
23
  ```
49
24
 
@@ -66,10 +41,5 @@ This is a hotly debated issue.
66
41
 
67
42
  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.
68
43
 
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).
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.
74
45
 
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.
@@ -10,8 +10,6 @@ module Pact
10
10
  attr_accessor :tmp_dir
11
11
  attr_accessor :reports_dir
12
12
  attr_writer :pactfile_write_mode
13
- attr_accessor :error_stream
14
- attr_accessor :output_stream
15
13
 
16
14
  def log_path
17
15
  log_dir + "/pact.log"
@@ -56,8 +54,6 @@ module Pact
56
54
  c.logger = default_logger c.log_path
57
55
  c.pactfile_write_mode = :overwrite
58
56
  c.reports_dir = File.expand_path('./reports/pacts')
59
- c.output_stream = $stdout
60
- c.error_stream = $stderr
61
57
  c
62
58
  end
63
59
 
@@ -84,7 +84,7 @@ module Pact
84
84
  end
85
85
 
86
86
  def warn_and_stderr msg
87
- Pact.configuration.error_stream.puts msg
87
+ $stderr.puts msg
88
88
  logger.warn msg
89
89
  end
90
90
 
@@ -43,9 +43,5 @@ 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
-
50
46
  end
51
47
  end
@@ -13,7 +13,7 @@ module Pact
13
13
  end
14
14
 
15
15
  def to_s
16
- diff_descriptions(diff).join("\n")
16
+ diff_descriptions(diff).join("\n").tap{|s| puts s}
17
17
  end
18
18
 
19
19
  def diff_descriptions obj, path = [], descriptions = []
@@ -1,12 +1,21 @@
1
- require 'pact/matchers/difference_indicator'
2
-
3
1
  module Pact
4
- class IndexNotFound < Pact::DifferenceIndicator
2
+ class IndexNotFound
3
+ def == other
4
+ other.is_a? IndexNotFound
5
+ end
5
6
 
6
7
  def to_s
7
8
  "<index not found>"
8
9
  end
9
10
 
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
+
10
19
  def empty?
11
20
  true
12
21
  end
@@ -40,7 +40,7 @@ module Pact
40
40
 
41
41
  class NoDiffIndicator
42
42
 
43
- def to_json
43
+ def to_json options = {}
44
44
  to_s
45
45
  end
46
46
 
@@ -58,6 +58,7 @@ module Pact
58
58
  DEFAULT_OPTIONS = {allow_unexpected_keys: true, structure: false}.freeze
59
59
 
60
60
  def diff expected, actual, opts = {}
61
+ # require 'pry'; pry(binding);
61
62
  options = DEFAULT_OPTIONS.merge(opts)
62
63
  case expected
63
64
  when Hash then hash_diff(expected, actual, options)
@@ -0,0 +1,48 @@
1
+ require 'pact/consumer_contract/active_support_support'
2
+ require 'colored'
3
+
4
+ module Pact
5
+ module Matchers
6
+ class NestedJsonDiffDecorator
7
+
8
+ include Pact::ActiveSupportSupport
9
+
10
+ EXPECTED = '"EXPECTED"'
11
+ EXPECTED_COLOURED = '"' + "expected".red + '"'
12
+ ACTUAL = '"ACTUAL"'
13
+ ACTUAL_COLOURED = '"' + "actual".green + '"'
14
+
15
+ attr_reader :diff
16
+
17
+ def initialize diff
18
+ @diff = diff
19
+ end
20
+
21
+ def to_hash
22
+ diff
23
+ end
24
+
25
+ def to_s
26
+ colourise_message_if_configured fix_json_formatting(diff.to_json)
27
+ end
28
+
29
+ def colourise_message_if_configured message
30
+ if Pact.configuration.color_enabled
31
+ colourise_message message
32
+ else
33
+ message
34
+ end
35
+ end
36
+
37
+ def colourise_message message
38
+ message.split("\n").collect{| line | colourise(line) }.join("\n")
39
+ end
40
+
41
+ def colourise line
42
+ line.white.gsub(EXPECTED, EXPECTED_COLOURED).gsub(ACTUAL, ACTUAL_COLOURED)
43
+ end
44
+
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,92 @@
1
+ module Pact
2
+ module Matchers
3
+
4
+ class PlusMinusDiffDecorator
5
+
6
+ def initialize diff
7
+ @diff = diff
8
+ end
9
+
10
+ def to_s
11
+ expected = generate_string(diff, :expected)
12
+ actual = generate_string(diff, :actual)
13
+
14
+ RSpec::Expectations::Differ.new.diff_as_string actual, expected
15
+ end
16
+
17
+ def handle thing, target
18
+ case thing
19
+ when Hash then copy_hash(thing, target)
20
+ when Array then copy_array(thing, target)
21
+ when Difference then copy_diff(thing, target)
22
+ when NoDiffIndicator then copy_no_diff(thing, target)
23
+ else copy_object(thing, target)
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def generate_string diff, target
30
+ comparable = handle(diff, target)
31
+ begin
32
+ # Can't think of an elegant way to check if we can pretty generate other than to try it and maybe fail
33
+ JSON.pretty_generate(comparable)
34
+ rescue JSON::GeneratorError
35
+ comparable.to_s
36
+ end
37
+ end
38
+
39
+ def copy_hash hash, target
40
+ hash.keys.each_with_object({}) do | key, new_hash |
41
+ value = handle hash[key], target
42
+ new_hash[key] = value unless (KeyNotFound === value || UnexpectedKey === value)
43
+ end
44
+ end
45
+
46
+ def copy_array array, target
47
+ array.each_index.each_with_object([]) do | index, new_array |
48
+ value = handle array[index], target
49
+ new_array[index] = value unless (UnexpectedIndex === value || IndexNotFound === value)
50
+ end
51
+ end
52
+
53
+ def copy_no_diff(thing, target)
54
+ thing
55
+ end
56
+
57
+ def copy_diff difference, target
58
+ if target == :actual
59
+ copy_object difference.actual, target
60
+ else
61
+ copy_object difference.expected, target
62
+ end
63
+ end
64
+
65
+ def copy_object object, target
66
+ if Regexp === object
67
+ RegexpDecorator.new(object)
68
+ else
69
+ object
70
+ end
71
+ end
72
+
73
+ class RegexpDecorator
74
+
75
+ def initialize regexp
76
+ @regexp = regexp
77
+ end
78
+
79
+ def to_json options={}
80
+ @regexp.inspect
81
+ end
82
+
83
+ def as_json
84
+ @regexp.inspect
85
+ end
86
+ end
87
+
88
+ attr_reader :diff
89
+ end
90
+
91
+ end
92
+ end