pact 1.1.0.rc2 → 1.1.0.rc3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (136) hide show
  1. data/.gitignore +0 -1
  2. data/CHANGELOG.md +46 -1
  3. data/Gemfile.lock +6 -4
  4. data/README.md +40 -186
  5. data/Rakefile +1 -1
  6. data/documentation/README.md +10 -0
  7. data/documentation/best-practices.md +33 -0
  8. data/documentation/configuration.md +166 -0
  9. data/documentation/diff_formatter_embedded.png +0 -0
  10. data/documentation/diff_formatter_list.png +0 -0
  11. data/documentation/diff_formatter_unix.png +0 -0
  12. data/documentation/faq.md +36 -6
  13. data/documentation/provider-states.md +173 -0
  14. data/documentation/raq.md +4 -4
  15. data/documentation/terminology.md +2 -2
  16. data/example/animal-service/Gemfile.lock +6 -9
  17. data/example/animal-service/Rakefile +2 -0
  18. data/example/animal-service/db/animal_db.sqlite3 +0 -0
  19. data/example/animal-service/lib/animal_service/animal_repository.rb +1 -5
  20. data/example/animal-service/lib/animal_service/api.rb +1 -1
  21. data/example/animal-service/spec/service_consumers/pact_helper.rb +7 -10
  22. data/example/animal-service/spec/service_consumers/provider_states_for_zoo_app.rb +5 -1
  23. data/example/zoo-app/Gemfile.lock +6 -9
  24. data/example/zoo-app/Rakefile +5 -0
  25. data/example/zoo-app/doc/markdown/README.md +3 -0
  26. data/example/zoo-app/doc/markdown/Zoo App - Animal Service.md +75 -0
  27. data/example/zoo-app/lib/zoo_app/animal_service_client.rb +2 -2
  28. data/example/zoo-app/spec/pacts/zoo_app-animal_service.json +1 -1
  29. data/example/zoo-app/spec/service_providers/animal_service_client_spec.rb +29 -34
  30. data/example/zoo-app/spec/service_providers/pact_helper.rb +4 -0
  31. data/lib/pact/configuration.rb +49 -1
  32. data/lib/pact/consumer/configuration.rb +4 -172
  33. data/lib/pact/consumer/configuration/configuration_extensions.rb +15 -0
  34. data/lib/pact/consumer/configuration/dsl.rb +12 -0
  35. data/lib/pact/consumer/configuration/mock_service.rb +89 -0
  36. data/lib/pact/consumer/configuration/service_consumer.rb +51 -0
  37. data/lib/pact/consumer/configuration/service_provider.rb +40 -0
  38. data/lib/pact/consumer/mock_service/interaction_mismatch.rb +3 -3
  39. data/lib/pact/consumer/mock_service/interaction_post.rb +2 -2
  40. data/lib/pact/consumer/mock_service/interaction_replay.rb +3 -4
  41. data/lib/pact/consumer/mock_service/verification_get.rb +32 -13
  42. data/lib/pact/consumer/rspec.rb +2 -4
  43. data/lib/pact/consumer/spec_hooks.rb +3 -1
  44. data/lib/pact/consumer_contract/consumer_contract.rb +1 -1
  45. data/lib/pact/consumer_contract/interaction.rb +1 -1
  46. data/lib/pact/doc/doc_file.rb +40 -0
  47. data/lib/pact/doc/generate.rb +11 -0
  48. data/lib/pact/doc/generator.rb +81 -0
  49. data/lib/pact/doc/interaction_view_model.rb +113 -0
  50. data/lib/pact/doc/markdown/generator.rb +26 -0
  51. data/lib/pact/doc/markdown/index_renderer.rb +41 -0
  52. data/lib/pact/doc/markdown/interaction.erb +14 -0
  53. data/lib/pact/doc/markdown/interaction_renderer.rb +38 -0
  54. data/lib/pact/doc/markdown/interactions_renderer.rb +56 -0
  55. data/lib/pact/doc/sort_interactions.rb +17 -0
  56. data/lib/pact/matchers/actual_type.rb +16 -0
  57. data/lib/pact/matchers/base_difference.rb +37 -0
  58. data/lib/pact/matchers/differ.rb +150 -0
  59. data/lib/pact/matchers/difference.rb +5 -30
  60. data/lib/pact/matchers/difference_indicator.rb +26 -0
  61. data/lib/pact/matchers/embedded_diff_formatter.rb +62 -0
  62. data/lib/pact/matchers/expected_type.rb +35 -0
  63. data/lib/pact/matchers/index_not_found.rb +3 -12
  64. data/lib/pact/matchers/{diff_decorator.rb → list_diff_formatter.rb} +28 -11
  65. data/lib/pact/matchers/matchers.rb +35 -39
  66. data/lib/pact/matchers/no_diff_indicator.rb +18 -0
  67. data/lib/pact/matchers/regexp_difference.rb +13 -0
  68. data/lib/pact/matchers/type_difference.rb +16 -0
  69. data/lib/pact/matchers/unexpected_index.rb +3 -13
  70. data/lib/pact/matchers/unexpected_key.rb +3 -12
  71. data/lib/pact/matchers/{plus_minus_diff_decorator.rb → unix_diff_formatter.rb} +22 -7
  72. data/lib/pact/provider/configuration.rb +5 -178
  73. data/lib/pact/provider/configuration/configuration_extension.rb +58 -0
  74. data/lib/pact/provider/configuration/dsl.rb +13 -0
  75. data/lib/pact/provider/configuration/pact_verification.rb +46 -0
  76. data/lib/pact/provider/configuration/service_provider_config.rb +16 -0
  77. data/lib/pact/provider/configuration/service_provider_dsl.rb +54 -0
  78. data/lib/pact/provider/matchers.rb +21 -13
  79. data/lib/pact/provider/matchers/messages.rb +43 -0
  80. data/lib/pact/provider/pact_spec_runner.rb +8 -0
  81. data/lib/pact/provider/rspec.rb +1 -1
  82. data/lib/pact/provider/rspec/formatter.rb +9 -7
  83. data/lib/pact/{consumer_contract → shared}/active_support_support.rb +4 -0
  84. data/lib/pact/shared/jruby_support.rb +18 -0
  85. data/lib/pact/shared/key_not_found.rb +3 -16
  86. data/lib/pact/shared/request.rb +5 -5
  87. data/lib/pact/term.rb +2 -2
  88. data/lib/pact/version.rb +1 -1
  89. data/pact.gemspec +2 -2
  90. data/spec/integration/pact/provider_configuration_spec.rb +2 -1
  91. data/spec/lib/pact/configuration_spec.rb +73 -0
  92. data/spec/lib/pact/consumer/mock_service/interaction_mismatch_spec.rb +21 -3
  93. data/spec/lib/pact/consumer/mock_service/verification_get_spec.rb +134 -0
  94. data/spec/lib/pact/consumer/request_spec.rb +1 -1
  95. data/spec/lib/pact/consumer_contract/active_support_support_spec.rb +1 -1
  96. data/spec/lib/pact/doc/generator_spec.rb +69 -0
  97. data/spec/lib/pact/doc/interaction_view_model_spec.rb +112 -0
  98. data/spec/lib/pact/doc/markdown/index_renderer_spec.rb +29 -0
  99. data/spec/lib/pact/doc/markdown/interactions_renderer_spec.rb +29 -0
  100. data/spec/lib/pact/matchers/differ_spec.rb +214 -0
  101. data/spec/lib/pact/matchers/difference_spec.rb +2 -12
  102. data/spec/lib/pact/matchers/embedded_diff_formatter_spec.rb +77 -0
  103. data/spec/lib/pact/matchers/index_not_found_spec.rb +21 -0
  104. data/spec/lib/pact/matchers/list_diff_formatter_spec.rb +114 -0
  105. data/spec/lib/pact/matchers/matchers_spec.rb +38 -22
  106. data/spec/lib/pact/matchers/regexp_difference_spec.rb +20 -0
  107. data/spec/lib/pact/matchers/type_difference_spec.rb +34 -0
  108. data/spec/lib/pact/matchers/unexpected_index_spec.rb +20 -0
  109. data/spec/lib/pact/matchers/unexpected_key_spec.rb +20 -0
  110. data/spec/lib/pact/matchers/{plus_minus_diff_decorator_spec.rb → unix_diff_formatter_spec.rb} +35 -6
  111. data/spec/lib/pact/provider/configuration/configuration_extension_spec.rb +30 -0
  112. data/spec/lib/pact/provider/configuration/pact_verification_spec.rb +43 -0
  113. data/spec/lib/pact/provider/configuration/service_provider_config_spec.rb +21 -0
  114. data/spec/lib/pact/provider/configuration/service_provider_dsl_spec.rb +92 -0
  115. data/spec/lib/pact/provider/configuration_spec.rb +7 -150
  116. data/spec/lib/pact/provider/matchers/messages_spec.rb +104 -0
  117. data/spec/lib/pact/provider/rspec/formatter_spec.rb +56 -0
  118. data/spec/lib/pact/shared/key_not_found_spec.rb +20 -0
  119. data/spec/lib/pact/shared/request_spec.rb +28 -0
  120. data/spec/spec_helper.rb +3 -0
  121. data/spec/standalone/consumer_fail_test.rb +54 -0
  122. data/spec/standalone/consumer_pass_test.rb +50 -0
  123. data/spec/support/generated_index.md +4 -0
  124. data/spec/support/generated_markdown.md +55 -0
  125. data/spec/support/interaction_view_model.json +63 -0
  126. data/spec/support/markdown_pact.json +48 -0
  127. data/spec/support/pact_helper.rb +2 -1
  128. data/spec/support/spec_support.rb +7 -0
  129. data/spec/support/test_app_fail.json +11 -2
  130. data/tasks/pact-test.rake +9 -0
  131. metadata +113 -20
  132. data/example/animal-service/db/animals_db.sqlite3 +0 -0
  133. data/lib/pact/consumer/rspec/full_example_description.rb +0 -28
  134. data/lib/pact/matchers/nested_json_diff_decorator.rb +0 -53
  135. data/spec/lib/pact/matchers/diff_decorator_spec.rb +0 -80
  136. data/spec/lib/pact/matchers/nested_json_diff_decorator_spec.rb +0 -48
data/Rakefile CHANGED
@@ -5,5 +5,5 @@ Dir.glob('lib/tasks/**/*.rake').each { |task| load task }
5
5
  Dir.glob('tasks/**/*.rake').each { |task| load task }
6
6
  RSpec::Core::RakeTask.new(:spec)
7
7
 
8
- task :default => [:spec, :spec_with_active_support, 'pact:tests']
8
+ task :default => [:spec, :spec_with_active_support, 'spec:standalone:pass', 'pact:tests']
9
9
 
@@ -0,0 +1,10 @@
1
+ ### Pact Documentation
2
+
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)
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
+ * [Testing with pact diagram](Testing with pact.png)
@@ -0,0 +1,33 @@
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.
@@ -0,0 +1,166 @@
1
+ # Configuration
2
+
3
+ ## Menu
4
+
5
+ #### Consumer and Provider configuration options
6
+ * [diff_formatter](#diff_formatter)
7
+ * [log_dir](#log_dir)
8
+ * [logger](#logger)
9
+ * [logger.level](#loggerlevel)
10
+
11
+ #### Consumer only configuration options
12
+ * [pact_dir](#pact_dir)
13
+ * [doc_dir](#doc_dir)
14
+ * [doc_generator](#doc_generator)
15
+ * [pactfile_write_mode](#pactfile_write_mode)
16
+
17
+ #### Provider only configuration options
18
+ * [include](#include)
19
+
20
+ ## Consumer and Provider
21
+
22
+ ### log_dir
23
+
24
+ ```ruby
25
+ Pact.configure do | config |
26
+ config.log_dir = './log'
27
+ end
28
+ ```
29
+
30
+ Default value: `./log`
31
+
32
+ ### logger
33
+
34
+ ```ruby
35
+ Pact.configure do | config |
36
+ config.logger = Logger.new
37
+ end
38
+ ```
39
+
40
+ Default value: file logger to the configured log_dir.
41
+
42
+ ### logger.level
43
+
44
+ ```ruby
45
+ Pact.configure do | config |
46
+ config.logger.level = Logger::INFO
47
+ end
48
+ ```
49
+
50
+ Default value: `Logger::DEBUG`
51
+
52
+ ### diff_formatter
53
+
54
+ ```ruby
55
+ Pact.configure do | config |
56
+ config.diff_formatter = :list
57
+ end
58
+ ```
59
+
60
+ Default value: [:list](#list)
61
+
62
+ Options: [:unix](#unix), [:list](#list), [:embedded](#embedded), [Custom Diff Formatter](#custom-diff-formatter)
63
+
64
+
65
+ #### :unix
66
+ <img src="diff_formatter_unix.png" width="700">
67
+
68
+ #### :list
69
+
70
+ <img src="diff_formatter_list.png" width="700">
71
+
72
+ #### :embedded
73
+
74
+ <img src="diff_formatter_embedded.png" width="700">
75
+
76
+
77
+ #### Custom Diff Formatter
78
+
79
+ Any object can be used that responds to `call`, accepting the argument `diff`.
80
+
81
+ ```ruby
82
+ class MyCustomDiffFormatter
83
+
84
+ def self.call diff
85
+ ### Do stuff here
86
+ end
87
+
88
+ end
89
+
90
+ Pact.configure do | config |
91
+ config.diff_formatter = MyCustomDiffFormatter
92
+ end
93
+ ```
94
+
95
+
96
+ ## Consumer
97
+
98
+ ### pact_dir
99
+
100
+ ```ruby
101
+ Pact.configure do | config |
102
+ config.pact_dir = `./spec/pacts`
103
+ end
104
+ ```
105
+
106
+ Default value: `./spec/pacts`
107
+
108
+ ### doc_generator
109
+
110
+ ```ruby
111
+ Pact.configure do | config |
112
+ config.doc_generator = :markdown
113
+ end
114
+ ```
115
+
116
+ Default value: none
117
+
118
+ Options: [:markdown](#markdown), [Custom Doc Generator](#custom-doc-generator)
119
+
120
+ #### :markdown
121
+
122
+ Generates Markdown documentation based on the contents of the pact files created in this consumer. Files are created in `${Pact.configuration.doc_dir}/markdown`.
123
+
124
+ #### Custom Doc Generator
125
+
126
+ Any object can be used that responds to `call`, accepting the arguments `pact_dir` and `doc_dir`.
127
+
128
+ ```ruby
129
+ Pact.configure do | config |
130
+ config.doc_generator = lambda{ | pact_dir, doc_dir | generate_some_docs(pact_dir, doc_dir) }
131
+ end
132
+
133
+ ```
134
+
135
+ #### doc_dir
136
+
137
+ ```ruby
138
+ Pact.configure do | config |
139
+ config.doc_dir = './doc'
140
+ end
141
+ ```
142
+
143
+ Default value: `./doc`
144
+
145
+
146
+ ### pactfile_write_mode
147
+
148
+ Default value: `:overwrite`
149
+ Options: `:overwrite`, `:update`, `:smart`
150
+
151
+ By default, the pact file will be overwritten (started from scratch) every time any rspec runs any spec using pacts. This means that if there are interactions that haven't been executed in the most recent rspec run, they are effectively removed from the pact file. If you have long running pact specs (e.g. they are generated using the browser with Capybara) and you are developing both consumer and provider in parallel, or trying to fix a broken interaction, it can be tedious to run all the specs at once. In this scenario, you can set the pactfile_write_mode to :update. This will keep all existing interactions, and update only the changed ones, identified by description and provider state. The down side of this is that if either of those fields change, the old interactions will not be removed from the pact file. As a middle path, you can set pactfile_write_mode to :smart. This will use :overwrite mode when running rake (as determined by a call to system using 'ps') and :update when running an individual spec.
152
+
153
+ ## Provider
154
+
155
+ Pact uses RSpec and Rack::Test to create dynamic specs based on the pact files. RSpec configuration can be used to modify test behaviour if there is not an appropriate Pact feature. If you wish to use the same spec_helper.rb file as your unit tests, require it in the pact_helper.rb, but remember that the RSpec configurations for your unit tests may or may not be what you want for your pact verification tests.
156
+
157
+ ### include
158
+
159
+ ```ruby
160
+ Pact.configure do | config |
161
+ config.include RSpec::Mocks::ExampleMethods
162
+ end
163
+ ```
164
+
165
+ To make modules available in the provider state set_up and tear_down blocks, include them in the configuration as shown below. One common use of this is to include RSpec::Mocks::ExampleMethods to make the `allow()` method available.
166
+
data/documentation/faq.md CHANGED
@@ -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.
@@ -0,0 +1,173 @@
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
+ ### Consumer codebase
6
+
7
+ For example, some code that creates a pact in a consumer project might look like this:
8
+
9
+ ```ruby
10
+ describe MyServiceProviderClient do
11
+
12
+ subject { MyServiceProviderClient.new }
13
+
14
+ describe "get_something" do
15
+ context "when a thing exists" do
16
+ before do
17
+ my_service.given("a thing exists").
18
+ upon_receiving("a request for a thing").with(method: 'get', path: '/thing').
19
+ will_respond_with(status: 200,
20
+ headers: { 'Content-Type' => 'application/json' },
21
+ body: { name: 'A small something'} )
22
+ end
23
+
24
+ it "returns a thing" do
25
+ expect(subject.get_something).to eq(SomethingModel.new('A small something'))
26
+ end
27
+ end
28
+
29
+ context "when a thing does not exist" do
30
+ before do
31
+ my_service.given("a thing does not exist").
32
+ upon_receiving("a request for a thing").with(method: 'get', path: '/thing').
33
+ will_respond_with(status: 404)
34
+ end
35
+
36
+ it "returns nil" do
37
+ expect(subject.get_something).to be_nil
38
+ end
39
+ end
40
+ end
41
+ end
42
+ ```
43
+
44
+ ### Provider codebase
45
+
46
+ 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.)
47
+
48
+ ```ruby
49
+ # In /spec/service_consumers/provider_states_for_my_service_consumer.rb
50
+
51
+ Pact.provider_states_for 'My Service Consumer' do
52
+
53
+ provider_state "a thing exists" do
54
+ set_up do
55
+ # Create a thing here using your framework of choice
56
+ # eg. Sequel.sqlite[:somethings].insert(name: "A small something")
57
+ end
58
+
59
+ tear_down do
60
+ # Any tear down steps to clean up your code
61
+ end
62
+ end
63
+
64
+ provider_state "a thing does not exist" do
65
+ no_op # If there's nothing to do because the state name is more for documentation purposes,
66
+ # you can use no_op to imply this.
67
+ end
68
+
69
+ end
70
+ ```
71
+ Require your provider states file in the `pact_helper.rb`
72
+
73
+ ```ruby
74
+ # In /spec/service_consumers/pact_helper.rb
75
+
76
+ require './spec/service_consumers/provider_states_for_my_service_consumer.rb'
77
+ ```
78
+
79
+ ### Base state
80
+
81
+ 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.
82
+
83
+ ```ruby
84
+ Pact.provider_states_for 'My Service Consumer' do
85
+
86
+ set_up do
87
+ # This will run before the set_up for provider state specified for the interaction.
88
+ # eg. create API user, set the expected basic auth details
89
+ end
90
+
91
+ tear_down do
92
+ # ...
93
+ # This will run after the tear_down for the specified provider state.
94
+ end
95
+ end
96
+ ```
97
+
98
+ ### Global state
99
+
100
+ 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.
101
+
102
+ ```ruby
103
+ Pact.set_up do
104
+ # eg. start database cleaner transaction
105
+ end
106
+
107
+ Pact.tear_down do
108
+ # eg. clean database
109
+ end
110
+ ```
111
+
112
+ ### Testing error responses
113
+
114
+ It is important to test how your client will handle error responses.
115
+
116
+ ```ruby
117
+ # Consumer codebase
118
+
119
+ describe MyServiceProviderClient do
120
+
121
+ subject { MyServiceProviderClient.new }
122
+
123
+ describe "get_something" do
124
+
125
+ context "when an error occurs retrieving a thing" do
126
+ before do
127
+ my_service.given("an error occurs while retrieving a thing").
128
+ upon_receiving("a request for a thing").with(method: 'get', path: '/thing').
129
+ will_respond_with(
130
+ status: 500,
131
+ headers: { 'Content-Type' => 'application/json' },
132
+ body: { message: "An error occurred!" } )
133
+ end
134
+
135
+ it "raises an error" do
136
+ expect{ subject.get_something }.to raise_error /An error occurred!/
137
+ end
138
+
139
+ end
140
+ end
141
+ end
142
+ ```
143
+
144
+ ```ruby
145
+ # Provider codebase
146
+
147
+ Pact.provider_states_for 'My Service Consumer' do
148
+ provider_state "an error occurs while retrieving a thing" do
149
+ set_up do
150
+ # Stubbing is ususally the easiest way to generate an error with predictable error text.
151
+ ThingRepository.stub(:find).and_raise("An error occurred!")
152
+ end
153
+ end
154
+ end
155
+ ```
156
+
157
+ ### Including modules for use in set_up and tear_down
158
+
159
+ To use RSpec's `allow()` syntax, include `RSpec::Mocks::ExampleMethods` in the configuration.
160
+
161
+ ```ruby
162
+ Pact.configure do | config |
163
+ config.include RSpec::Mocks::ExampleMethods
164
+ end
165
+ ```
166
+
167
+ Any modules included this way will be available in the set_up and tear_down blocks eg. test data helper methods.
168
+
169
+ ```ruby
170
+ Pact.configure do | config |
171
+ config.include MyFactoryMethods
172
+ end
173
+ ```