pact 1.3.0 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/.travis.yml +1 -1
  2. data/CHANGELOG.md +14 -0
  3. data/Gemfile.lock +5 -5
  4. data/README.md +25 -29
  5. data/documentation/README.md +9 -7
  6. data/lib/pact/configuration.rb +83 -12
  7. data/lib/pact/consumer/mock_service/interaction_mismatch.rb +5 -1
  8. data/lib/pact/consumer/spec_hooks.rb +7 -4
  9. data/lib/pact/consumer/world.rb +12 -0
  10. data/lib/pact/consumer_contract/headers.rb +51 -0
  11. data/lib/pact/consumer_contract/request.rb +6 -1
  12. data/lib/pact/provider/configuration/service_provider_dsl.rb +6 -1
  13. data/lib/pact/provider/matchers/messages.rb +2 -2
  14. data/lib/pact/provider/rspec.rb +7 -1
  15. data/lib/pact/provider/rspec/backtrace_formatter.rb +30 -6
  16. data/lib/pact/provider/rspec/matchers.rb +9 -6
  17. data/lib/pact/shared/json_differ.rb +15 -0
  18. data/lib/pact/shared/request.rb +11 -2
  19. data/lib/pact/shared/text_differ.rb +14 -0
  20. data/lib/pact/version.rb +1 -1
  21. data/spec/lib/pact/configuration_spec.rb +127 -5
  22. data/spec/lib/pact/consumer/mock_service/interaction_mismatch_spec.rb +8 -5
  23. data/spec/lib/pact/consumer_contract/headers_spec.rb +107 -0
  24. data/spec/lib/pact/consumer_contract/request_spec.rb +9 -0
  25. data/spec/lib/pact/matchers/matchers_spec.rb +35 -0
  26. data/spec/lib/pact/provider/configuration/service_provider_dsl_spec.rb +16 -0
  27. data/spec/lib/pact/provider/matchers/messages_spec.rb +5 -4
  28. data/spec/lib/pact/provider/rspec_spec.rb +15 -11
  29. data/spec/lib/pact/shared/json_differ_spec.rb +36 -0
  30. data/spec/lib/pact/shared/request_spec.rb +25 -1
  31. data/spec/lib/pact/shared/text_differ_spec.rb +54 -0
  32. data/spec/support/pact_helper.rb +2 -0
  33. data/spec/support/test_app_with_right_content_type_differ.json +23 -0
  34. data/tasks/pact-test.rake +6 -0
  35. metadata +72 -30
  36. checksums.yaml +0 -7
  37. data/documentation/Testing with pact.png +0 -0
  38. data/documentation/best-practices.md +0 -33
  39. data/documentation/development-workflow.md +0 -22
  40. data/documentation/faq.md +0 -81
  41. data/documentation/provider-states.md +0 -178
  42. data/documentation/raq.md +0 -39
  43. data/documentation/terminology.md +0 -25
  44. data/documentation/troubleshooting.md +0 -4
  45. data/documentation/verifying-pacts.md +0 -106
data/.travis.yml CHANGED
@@ -5,4 +5,4 @@ rvm:
5
5
  - 2.1.1
6
6
  - jruby-19mode
7
7
  - jruby-1.7.11
8
- - jruby-head
8
+ - jruby-1.7.12
data/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@ Do this to generate your change history
2
2
 
3
3
  git log --pretty=format:' * %h - %s (%an, %ad)'
4
4
 
5
+ ### 1.3.1 (11 August 2014)
6
+
7
+ * 3432259 - Fixed 'pact:verify broken with rspec-core 3.0.3' https://github.com/realestate-com-au/pact/issues/44 (bethesque, Mon Aug 11 10:14:42 2014 +1000)
8
+ * e2e8eff - Deleted documentation that has been moved to the wiki (bethesque, Thu Jul 24 15:20:07 2014 +1000)
9
+ * bcc3143 - Fixing bug 'Method case should not matter when matching requests' https://github.com/realestate-com-au/pact/issues/41 (bethesque, Tue Jul 22 16:51:48 2014 +1000)
10
+ * d4bfab9 - Adding ability to configure DiffFormatter based on content-type (bethesque, Mon Jun 23 21:22:47 2014 +1000)
11
+ * eb330ea - Ensured content-type header works in a case insensitive way when looking up the right differ (bethesque, Mon Jun 23 17:23:04 2014 +1000)
12
+ * 2733e8e - Made header matching case insensitive for requests. Fixing issue https://github.com/realestate-com-au/pact/issues/20 (bethesque, Mon May 26 19:15:48 2014 +1000)
13
+ * 2b8355d - Added nicer error message for scenario when a service provider app has not been configured, and there is no config.ru (bethesque, Mon Jun 23 09:42:18 2014 +1000)
14
+ * 1e774bb - Defaulting to TextDiffer if response has no content-type (bethesque, Sat Jun 21 10:34:44 2014 +1000)
15
+ * 863b093 - Added support for documents without content types (bethesque, Sat Jun 21 10:32:08 2014 +1000)
16
+ * b21900b - Enabling differs to be configured based on Content-Type (bethesque, Sat Jun 21 10:21:37 2014 +1000)
17
+ * 527b5d5 - Modified after hook to only write pacts when one or more 'pact => true' examples have run (bethesque, Wed Jun 18 15:15:55 2014 +1000)
18
+
5
19
  ### 1.3.0 (18 June 2014)
6
20
 
7
21
  * ea79190 - Modifying (cough*monkeypatching*cough) RSpec::Core::BacktraceFormatter as RSpec3 has some hardcoded exclusion patterns that result in *all* the backtrace lines being shown when pact:verify fails. (bethesque, Wed Jun 18 13:02:38 2014 +1000)
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pact (1.3.0)
4
+ pact (1.3.1)
5
5
  awesome_print (~> 1.1)
6
6
  find_a_port (~> 1.0.1)
7
7
  json
@@ -50,14 +50,14 @@ GEM
50
50
  rspec-core (~> 3.0.0)
51
51
  rspec-expectations (~> 3.0.0)
52
52
  rspec-mocks (~> 3.0.0)
53
- rspec-core (3.0.1)
53
+ rspec-core (3.0.3)
54
54
  rspec-support (~> 3.0.0)
55
- rspec-expectations (3.0.1)
55
+ rspec-expectations (3.0.3)
56
56
  diff-lcs (>= 1.2.0, < 2.0)
57
57
  rspec-support (~> 3.0.0)
58
- rspec-mocks (3.0.1)
58
+ rspec-mocks (3.0.3)
59
59
  rspec-support (~> 3.0.0)
60
- rspec-support (3.0.0)
60
+ rspec-support (3.0.3)
61
61
  safe_yaml (1.0.3)
62
62
  slop (3.5.0)
63
63
  term-ansicolor (1.3.0)
data/README.md CHANGED
@@ -14,6 +14,12 @@ Travis CI Status: [![travis-ci.org Build Status](https://travis-ci.org/realestat
14
14
 
15
15
  Pact is most valuable for designing and testing integrations where you (or your team/organisation/partner organisation) control the development of both the consumer and the provider. It is fantastic tool for intra-organsation microservices.
16
16
 
17
+ ## What is it not good for?
18
+
19
+ * Performance and load testing.
20
+ * Functional testing of the provider - that is what the provider's own tests should do. Pact is about checking the contents and format of requests and responses.
21
+ * Situations where you cannot load data into the provider without using the API that you're actually testing.
22
+
17
23
  ## Features
18
24
 
19
25
  * A service is mocked using an actual process running on a specified port, so javascript clients can be tested as easily as backend clients.
@@ -27,7 +33,7 @@ Pact is most valuable for designing and testing integrations where you (or your
27
33
  ## How does it work?
28
34
 
29
35
  1. In the specs for the provider facing code in the consumer project, expectations are set up on a mock service provider.
30
- 1. When the specs are run, the requests, and their expected responses, are written to a "pact" file.
36
+ 1. When the specs are run, the mock service returns the expected responses. The requests, and their expected responses, are then written to a "pact" file.
31
37
  1. The requests in the pact file are later replayed against the provider, and the actual responses are checked to make sure they match the expected responses.
32
38
 
33
39
  ## Why is developing and testing with Pact better than using traditional system integration tests?
@@ -37,6 +43,7 @@ Pact is most valuable for designing and testing integrations where you (or your
37
43
  * Causes of failure are easier to identify as only one component is being tested at a time.
38
44
  * 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.
39
45
  * No separate integration environment required for automated integration tests - pact tests run in standalone CI builds.
46
+ * Integration flows that would traditionally require running multiple services at the same time can be broken down and each integration point tested separately.
40
47
 
41
48
  ## Contact
42
49
 
@@ -180,7 +187,7 @@ require 'pact/tasks'
180
187
 
181
188
  Create a `pact_helper.rb` in your service provider project. The recommended place is `spec/service_consumers/pact_helper.rb`.
182
189
 
183
- See [Verifying Pacts](documentation/verifying-pacts.md) and the [Provider](documentation/configuration.md#provider) section of the Configuration documentation for more information.
190
+ See [Verifying Pacts](https://github.com/realestate-com-au/pact/wiki/Verifying-pacts) and the [Provider](documentation/configuration.md#provider) section of the Configuration documentation for more information.
184
191
 
185
192
  ```ruby
186
193
  # In specs/service_consumers/pact_helper.rb
@@ -220,29 +227,8 @@ Yay! Your provider now honours the pact it has with your consumer. You can now h
220
227
 
221
228
  ### Using provider states
222
229
 
223
- 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.
224
-
225
- Read more about provider states [here](/documentation/provider-states.md).
226
-
227
- ### Verifying pacts
230
+ Each interaction in a pact is verified in isolation, with no context maintained from the previous interactions. So how do you test a request that requires data to already exist on the provider? Read about provider states [here](https://github.com/realestate-com-au/pact/wiki/Provider-states).
228
231
 
229
- You can verify a pact at an arbitrary local or remote URL
230
-
231
- $ rake pact:verify:at[../path-to-your-consumer-project/specs/pacts/my_consumer-my_provider.json]
232
- $ rake pact:verify:at[http://build-box/MyConsumerBuild/latestSuccessful/artifacts/my_consumer-my_provider.json]
233
-
234
- To make a shortcut task for pact at an arbitrary URL, add the following to your Rakefile. The pact.uri may be a local file system path or a remote URL.
235
-
236
- ```ruby
237
- # In Rakefile or /tasks/pact.rake
238
-
239
- # This creates a rake task that can be executed by running
240
- # $ rake pact:verify:dev
241
-
242
- Pact::VerificationTask.new(:dev) do | pact |
243
- pact.uri '../path-to-your-consumer-project/specs/pacts/my_consumer-my_provider.json'
244
- end
245
- ```
246
232
 
247
233
  ## Configuration
248
234
 
@@ -250,15 +236,20 @@ See the [Configuration](/documentation/configuration.md) section of the document
250
236
 
251
237
  ## Pact best practices
252
238
 
253
- As in all things, there are good ways to implement Pacts, and there are not so good ways. Check out the [Best practices](/documentation/best-practices.md) section of the documentation to make sure you're not Pacting it Wrong.
239
+ As in all things, there are good ways to implement Pacts, and there are not so good ways. Check out the [Best practices](https://github.com/realestate-com-au/pact/wiki/Best-practices) section of the documentation to make sure you're not Pacting it Wrong.
254
240
 
255
241
  ## Docs
256
242
 
257
243
  * [Example](example)
258
- * [Frequently Asked Questions](documentation/faq.md)
259
- * [Rarely Asked Questions](documentation/raq.md)
260
- * [Terminology](documentation/terminology.md)
261
- * [Configuration](/documentation/configuration.md)
244
+ * [Configuration](documentation/configuration.md)
245
+ * [Terminology](https://github.com/realestate-com-au/pact/wiki/Terminology)
246
+ * [Provider States](https://github.com/realestate-com-au/pact/wiki/Provider-states)
247
+ * [Verifying pacts](https://github.com/realestate-com-au/pact/wiki/Verifying-pacts)
248
+ * [Frequently asked questions](https://github.com/realestate-com-au/pact/wiki/FAQ)
249
+ * [Rarely asked questions](https://github.com/realestate-com-au/pact/wiki/RAQ)
250
+ * [Best practices](https://github.com/realestate-com-au/pact/wiki/Best-practices)
251
+ * [Troubleshooting](https://github.com/realestate-com-au/pact/wiki/Troubleshooting)
252
+ * [Testing with pact diagram](https://github.com/realestate-com-au/pact/wiki/Testing with pact.png)
262
253
 
263
254
  ## Related libraries
264
255
 
@@ -270,6 +261,8 @@ As in all things, there are good ways to implement Pacts, and there are not so g
270
261
 
271
262
  [Pact JVM](https://github.com/DiUS/pact-jvm) - A Pact implementation for the JVM (Java and Scala). It generates pact files that are compatible with the Ruby implementation.
272
263
 
264
+ [Pact .NET](https://github.com/SEEK-Jobs/pact-net) - A Pact implementation for .NET.
265
+
273
266
  [Shokkenki](https://github.com/brentsnook/shokkenki) - Another Consumer Driven Contract gem written by one of Pact's original authors, Brent Snook. Shokkenki allows matchers to be composed using jsonpath expressions and allows auto-generation of mock response values based on regular expressions.
274
267
 
275
268
  ## Links
@@ -301,3 +294,6 @@ Long term:
301
294
  3. Commit your changes (`git commit -am 'Add some feature'`)
302
295
  4. Push to the branch (`git push origin my-new-feature`)
303
296
  5. Create new Pull Request
297
+
298
+ If you would like to implement pact in another language, please check out the [Pact specification](https://github.com/bethesque/pact-specification) and have a chat to one of us on the [pact-dev Google group](https://groups.google.com/forum/#!forum/pact-dev). The vision is to have a compatible pact implementation in all the commonly used languages, your help would be greatly appreciated!
299
+
@@ -1,11 +1,13 @@
1
1
  ### Pact Documentation
2
2
 
3
3
  * Step by step instructions for getting started with pacts can be found in the project [README.md](/README.md#usage)
4
- * [Terminology](terminology.md)
4
+ * [Terminology](https://github.com/realestate-com-au/pact/wiki/Terminology)
5
5
  * [Configuration](configuration.md)
6
- * [Provider States](provider-states.md)
7
- * [Frequently asked questions](faq.md)
8
- * [Rarely asked questions](raq.md)
9
- * [Best practices](best-practices.md)
10
- * [Troubleshooting](troubleshooting.md)
11
- * [Testing with pact diagram](Testing with pact.png)
6
+ * [Provider States](https://github.com/realestate-com-au/pact/wiki/Provider-states)
7
+ * [Verifying pacts](https://github.com/realestate-com-au/pact/wiki/Verifying-pacts)
8
+ * [Frequently asked questions](https://github.com/realestate-com-au/pact/wiki/FAQ)
9
+ * [Rarely asked questions](https://github.com/realestate-com-au/pact/wiki/RAQ)
10
+ * [Best practices](https://github.com/realestate-com-au/pact/wiki/Best-practices)
11
+ * [Troubleshooting](https://github.com/realestate-com-au/pact/wiki/Troubleshooting)
12
+ * [Testing with pact diagram](https://github.com/realestate-com-au/pact/wiki/Testing with pact.png)
13
+ * [Development workflow](https://github.com/realestate-com-au/pact/wiki/Development-workflow)
@@ -4,6 +4,8 @@ require 'pact/doc/markdown/generator'
4
4
  require 'pact/matchers/unix_diff_formatter'
5
5
  require 'pact/matchers/embedded_diff_formatter'
6
6
  require 'pact/matchers/list_diff_formatter'
7
+ require 'pact/shared/json_differ'
8
+ require 'pact/shared/text_differ'
7
9
 
8
10
  module Pact
9
11
 
@@ -17,6 +19,27 @@ module Pact
17
19
  :list => Pact::Matchers::ListDiffFormatter
18
20
  }
19
21
 
22
+
23
+ class NilMatcher
24
+ def self.=~ other
25
+ other == nil ? 0 : nil
26
+ end
27
+ end
28
+
29
+ DIFF_FORMATTER_REGISTRATIONS = [
30
+ [/.*/, Pact::Matchers::UnixDiffFormatter],
31
+ [NilMatcher, Pact::Matchers::UnixDiffFormatter]
32
+ ]
33
+
34
+ DIFFERS = [
35
+ [/json/, Pact::JsonDiffer],
36
+ [NilMatcher, Pact::TextDiffer],
37
+ [/.*/, Pact::TextDiffer]
38
+ ]
39
+
40
+
41
+ DEFAULT_DIFFER = Pact::TextDiffer
42
+
20
43
  attr_accessor :pact_dir
21
44
  attr_accessor :log_dir
22
45
  attr_accessor :doc_dir
@@ -41,6 +64,11 @@ module Pact
41
64
  c
42
65
  end
43
66
 
67
+ def initialize
68
+ @differ_registrations = []
69
+ @diff_formatter_registrations = []
70
+ end
71
+
44
72
  def logger
45
73
  @logger ||= create_logger
46
74
  end
@@ -61,20 +89,29 @@ module Pact
61
89
  @doc_generators ||= []
62
90
  end
63
91
 
64
- def diff_formatter
65
- @diff_formatter ||= DIFF_FORMATTERS[:unix]
92
+ # Should this be deprecated in favour of register_diff_formatter???
93
+ def diff_formatter= diff_formatter
94
+ register_diff_formatter /.*/, diff_formatter
95
+ register_diff_formatter nil, diff_formatter
66
96
  end
67
97
 
68
- def diff_formatter= diff_formatter
69
- @diff_formatter = begin
70
- if DIFF_FORMATTERS[diff_formatter]
71
- DIFF_FORMATTERS[diff_formatter]
72
- elsif diff_formatter.respond_to?(:call)
73
- diff_formatter
74
- else
75
- raise "Pact.configuration.diff_formatter needs to respond to call, or be in the preconfigured list: #{DIFF_FORMATTERS.keys}"
76
- end
77
- end
98
+ def register_diff_formatter content_type, diff_formatter
99
+ key = content_type_regexp_for content_type
100
+ @diff_formatter_registrations << [key, diff_formatter_for(diff_formatter)]
101
+ end
102
+
103
+ def diff_formatter_for_content_type content_type
104
+ diff_formatter_registrations.find{ | registration | registration.first =~ content_type }.last
105
+ end
106
+
107
+ def register_body_differ content_type, differ
108
+ key = content_type_regexp_for content_type
109
+ validate_differ differ
110
+ @differ_registrations << [key, differ]
111
+ end
112
+
113
+ def body_differ_for_content_type content_type
114
+ differ_registrations.find{ | registration | registration.first =~ content_type }.last
78
115
  end
79
116
 
80
117
  def log_path
@@ -91,6 +128,40 @@ module Pact
91
128
 
92
129
  private
93
130
 
131
+ def diff_formatter_for input
132
+ if DIFF_FORMATTERS[input]
133
+ DIFF_FORMATTERS[input]
134
+ elsif input.respond_to?(:call)
135
+ input
136
+ else
137
+ raise "Pact diff_formatter needs to respond to call, or be in the preconfigured list: #{DIFF_FORMATTERS.keys}"
138
+ end
139
+ end
140
+
141
+ def validate_differ differ
142
+ if !differ.respond_to?(:call)
143
+ raise "Pact.configuration.register_body_differ expects a differ that is a lamda or a class/object that responds to call."
144
+ end
145
+ end
146
+
147
+ def content_type_regexp_for content_type
148
+ case content_type
149
+ when String then Regexp.new(/^#{Regexp.escape(content_type)}$/)
150
+ when Regexp then content_type
151
+ when nil then NilMatcher
152
+ else
153
+ raise "Invalid content type used to register a differ (#{content_type.inspect}). Please use a Regexp or a String."
154
+ end
155
+ end
156
+
157
+ def differ_registrations
158
+ @differ_registrations + DIFFERS
159
+ end
160
+
161
+ def diff_formatter_registrations
162
+ @diff_formatter_registrations + DIFF_FORMATTER_REGISTRATIONS
163
+ end
164
+
94
165
  def self.default_log_dir
95
166
  File.expand_path("./log")
96
167
  end
@@ -56,10 +56,14 @@ module Pact
56
56
  def to_s
57
57
  [
58
58
  "Diff with interaction: #{candidate_interaction.description_with_provider_state_quoted}",
59
- Pact.configuration.diff_formatter.call(diff, {colour: false})
59
+ diff_formatter.call(diff, {colour: false})
60
60
  ].join("\n")
61
61
  end
62
62
 
63
+ def diff_formatter
64
+ Pact.configuration.diff_formatter_for_content_type(candidate_interaction.request.content_type)
65
+ end
66
+
63
67
  def diff
64
68
  @diff ||= candidate_interaction.request.difference(actual_request)
65
69
  end
@@ -11,6 +11,7 @@ module Pact
11
11
  end
12
12
 
13
13
  def before_each example_description
14
+ Pact.consumer_world.register_pact_example_ran
14
15
  Pact.configuration.logger.info "Clearing all expectations"
15
16
  Pact::Consumer::AppManager.instance.ports_of_mock_services.each do | port |
16
17
  Pact::Consumer::MockServiceClient.clear_interactions port, example_description
@@ -25,10 +26,12 @@ module Pact
25
26
  end
26
27
 
27
28
  def after_suite
28
- Pact.consumer_world.consumer_contract_builders.each { | c | c.write_pact }
29
- Pact::Doc::Generate.call
30
- Pact::Consumer::AppManager.instance.kill_all
31
- Pact::Consumer::AppManager.instance.clear_all
29
+ if Pact.consumer_world.any_pact_examples_ran?
30
+ Pact.consumer_world.consumer_contract_builders.each { | c | c.write_pact }
31
+ Pact::Doc::Generate.call
32
+ Pact::Consumer::AppManager.instance.kill_all
33
+ Pact::Consumer::AppManager.instance.clear_all
34
+ end
32
35
  end
33
36
  end
34
37
  end
@@ -12,6 +12,10 @@ module Pact
12
12
  module Consumer
13
13
  class World
14
14
 
15
+ def initialize
16
+ @any_pact_examples_ran = false
17
+ end
18
+
15
19
  def consumer_contract_builders
16
20
  @consumer_contract_builders ||= []
17
21
  end
@@ -20,6 +24,14 @@ module Pact
20
24
  consumer_contract_builders << consumer_contract_builder
21
25
  end
22
26
 
27
+ def register_pact_example_ran
28
+ @any_pact_examples_ran = true
29
+ end
30
+
31
+ def any_pact_examples_ran?
32
+ @any_pact_examples_ran
33
+ end
34
+
23
35
  end
24
36
  end
25
37
  end
@@ -0,0 +1,51 @@
1
+ module Pact
2
+
3
+ class DuplicateHeaderError < StandardError; end
4
+ class InvalidHeaderNameTypeError < StandardError; end
5
+
6
+ class Headers < Hash
7
+
8
+ def initialize hash = {}
9
+ hash.each_pair do | key, value |
10
+ check_for_invalid key
11
+ self[find_matching_key(key)] = value
12
+ end
13
+ self.freeze
14
+ end
15
+
16
+ def [] key
17
+ super(find_matching_key(key))
18
+ end
19
+
20
+ def fetch *args, &block
21
+ args[0] = find_matching_key(args[0]) if args.first
22
+ super(*args, &block)
23
+ end
24
+
25
+ def key? key
26
+ super(find_matching_key(key))
27
+ end
28
+
29
+ alias_method :has_key?, :key?
30
+ alias_method :include?, :key?
31
+
32
+ private
33
+
34
+ def find_matching_key key
35
+ key = key.to_s
36
+ match = keys.find { |k| k.downcase == key.downcase }
37
+ match.nil? ? key : match
38
+ end
39
+
40
+ def check_for_invalid key
41
+ unless (String === key || Symbol === key)
42
+ raise InvalidHeaderNameTypeError.new "Header name (#{key}) must be a String or a Symbol."
43
+ end
44
+ if key? key
45
+ raise DuplicateHeaderError.new "Duplicate header found (#{find_matching_key(key)} and #{key}. Please use a comma separated single value when multiple headers with the same name are required."
46
+ end
47
+ end
48
+
49
+ end
50
+
51
+ end
@@ -60,8 +60,13 @@ module Pact
60
60
  end
61
61
 
62
62
  def body_difference(actual_body)
63
- diff({:body => body}, {body: actual_body}, allow_unexpected_keys: runtime_options[:allow_unexpected_keys_in_body])
63
+ body_differ.call({:body => body}, {body: actual_body}, allow_unexpected_keys: runtime_options[:allow_unexpected_keys_in_body])
64
64
  end
65
+
66
+ def body_differ
67
+ Pact.configuration.body_differ_for_content_type content_type
68
+ end
69
+
65
70
  end
66
71
 
67
72
  end