pacto 0.3.0.pre → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.rubocop-todo.yml +0 -27
- data/.rubocop.yml +9 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +4 -5
- data/CONTRIBUTING.md +112 -0
- data/Gemfile +5 -0
- data/Guardfile +18 -13
- data/README.md +157 -101
- data/Rakefile +3 -3
- data/features/configuration/strict_matchers.feature +97 -0
- data/features/evolve/README.md +11 -0
- data/features/evolve/existing_services.feature +82 -0
- data/features/generate/README.md +5 -0
- data/features/generate/generation.feature +28 -0
- data/features/steps/pacto_steps.rb +75 -0
- data/features/stub/README.md +2 -0
- data/features/stub/templates.feature +46 -0
- data/features/support/env.rb +11 -5
- data/features/validate/README.md +1 -0
- data/features/validate/body_only.feature +85 -0
- data/features/{journeys/validation.feature → validate/meta_validation.feature} +41 -24
- data/features/validate/validation.feature +36 -0
- data/lib/pacto.rb +61 -33
- data/lib/pacto/contract.rb +18 -15
- data/lib/pacto/contract_factory.rb +14 -11
- data/lib/pacto/contract_files.rb +17 -0
- data/lib/pacto/contract_list.rb +17 -0
- data/lib/pacto/contract_validator.rb +29 -0
- data/lib/pacto/core/configuration.rb +19 -17
- data/lib/pacto/core/contract_registry.rb +43 -0
- data/lib/pacto/core/{callback.rb → hook.rb} +3 -3
- data/lib/pacto/core/modes.rb +33 -0
- data/lib/pacto/core/validation_registry.rb +45 -0
- data/lib/pacto/erb_processor.rb +0 -1
- data/lib/pacto/extensions.rb +18 -4
- data/lib/pacto/generator.rb +34 -49
- data/lib/pacto/generator/filters.rb +41 -0
- data/lib/pacto/hooks/erb_hook.rb +4 -3
- data/lib/pacto/logger.rb +4 -2
- data/lib/pacto/meta_schema.rb +4 -2
- data/lib/pacto/rake_task.rb +28 -25
- data/lib/pacto/request_clause.rb +43 -0
- data/lib/pacto/request_pattern.rb +8 -0
- data/lib/pacto/response_clause.rb +15 -0
- data/lib/pacto/rspec.rb +102 -0
- data/lib/pacto/stubs/uri_pattern.rb +23 -0
- data/lib/pacto/stubs/webmock_adapter.rb +69 -0
- data/lib/pacto/stubs/webmock_helper.rb +71 -0
- data/lib/pacto/ui.rb +7 -0
- data/lib/pacto/uri.rb +9 -0
- data/lib/pacto/validation.rb +57 -0
- data/lib/pacto/validators/body_validator.rb +41 -0
- data/lib/pacto/validators/request_body_validator.rb +23 -0
- data/lib/pacto/validators/response_body_validator.rb +23 -0
- data/lib/pacto/validators/response_header_validator.rb +49 -0
- data/lib/pacto/validators/response_status_validator.rb +24 -0
- data/lib/pacto/version.rb +1 -1
- data/pacto.gemspec +33 -29
- data/resources/contract_schema.json +8 -176
- data/resources/draft-03.json +174 -0
- data/spec/integration/data/strict_contract.json +2 -2
- data/spec/integration/e2e_spec.rb +22 -31
- data/spec/integration/rspec_spec.rb +94 -0
- data/spec/integration/templating_spec.rb +9 -12
- data/{lib → spec}/pacto/server.rb +0 -0
- data/{lib → spec}/pacto/server/dummy.rb +11 -8
- data/{lib → spec}/pacto/server/playback_servlet.rb +1 -1
- data/spec/spec_helper.rb +2 -0
- data/spec/unit/hooks/erb_hook_spec.rb +15 -15
- data/spec/unit/pacto/configuration_spec.rb +2 -10
- data/spec/unit/pacto/contract_factory_spec.rb +16 -13
- data/spec/unit/pacto/contract_files_spec.rb +42 -0
- data/spec/unit/pacto/contract_list_spec.rb +35 -0
- data/spec/unit/pacto/contract_spec.rb +43 -44
- data/spec/unit/pacto/contract_validator_spec.rb +85 -0
- data/spec/unit/pacto/core/configuration_spec.rb +4 -11
- data/spec/unit/pacto/core/contract_registry_spec.rb +119 -0
- data/spec/unit/pacto/core/modes_spec.rb +18 -0
- data/spec/unit/pacto/core/validation_registry_spec.rb +76 -0
- data/spec/unit/pacto/core/validation_spec.rb +60 -0
- data/spec/unit/pacto/extensions_spec.rb +14 -23
- data/spec/unit/pacto/generator/filters_spec.rb +99 -0
- data/spec/unit/pacto/generator_spec.rb +34 -73
- data/spec/unit/pacto/meta_schema_spec.rb +46 -6
- data/spec/unit/pacto/pacto_spec.rb +17 -15
- data/spec/unit/pacto/{request_spec.rb → request_clause_spec.rb} +32 -44
- data/spec/unit/pacto/request_pattern_spec.rb +22 -0
- data/spec/unit/pacto/response_clause_spec.rb +54 -0
- data/spec/unit/pacto/stubs/uri_pattern_spec.rb +28 -0
- data/spec/unit/pacto/stubs/webmock_adapter_spec.rb +205 -0
- data/spec/unit/pacto/stubs/webmock_helper_spec.rb +20 -0
- data/spec/unit/pacto/uri_spec.rb +20 -0
- data/spec/unit/pacto/validators/body_validator_spec.rb +105 -0
- data/spec/unit/pacto/validators/response_header_validator_spec.rb +94 -0
- data/spec/unit/pacto/validators/response_status_validator_spec.rb +20 -0
- metadata +230 -146
- data/features/generation/generation.feature +0 -25
- data/lib/pacto/core/contract_repository.rb +0 -44
- data/lib/pacto/hash_merge_processor.rb +0 -14
- data/lib/pacto/request.rb +0 -57
- data/lib/pacto/response.rb +0 -63
- data/lib/pacto/response_adapter.rb +0 -24
- data/lib/pacto/stubs/built_in.rb +0 -57
- data/spec/unit/pacto/core/contract_repository_spec.rb +0 -133
- data/spec/unit/pacto/hash_merge_processor_spec.rb +0 -20
- data/spec/unit/pacto/response_adapter_spec.rb +0 -25
- data/spec/unit/pacto/response_spec.rb +0 -201
- data/spec/unit/pacto/stubs/built_in_spec.rb +0 -168
data/Rakefile
CHANGED
@@ -5,17 +5,17 @@ require 'cucumber'
|
|
5
5
|
require 'cucumber/rake/task'
|
6
6
|
require 'coveralls/rake/task'
|
7
7
|
require 'rubocop/rake_task'
|
8
|
+
require 'rake/notes/rake_task'
|
8
9
|
|
9
10
|
Coveralls::RakeTask.new
|
10
11
|
|
11
12
|
Rubocop::RakeTask.new(:rubocop) do |task|
|
12
|
-
task.patterns = ['**/*.rb', 'Rakefile']
|
13
13
|
# abort rake on failure
|
14
|
-
task.fail_on_error =
|
14
|
+
task.fail_on_error = true
|
15
15
|
end
|
16
16
|
|
17
17
|
Cucumber::Rake::Task.new(:journeys) do |t|
|
18
|
-
t.cucumber_opts = 'features --format
|
18
|
+
t.cucumber_opts = 'features --format progress'
|
19
19
|
end
|
20
20
|
|
21
21
|
RSpec::Core::RakeTask.new(:unit) do |t|
|
@@ -0,0 +1,97 @@
|
|
1
|
+
Feature: Strict Matching
|
2
|
+
|
3
|
+
By default, Pacto matches requests to contracts (and stubs) using exact request paths, parameters, and headers. This strict behavior is useful for Consumer-Driven Contracts.
|
4
|
+
|
5
|
+
You can use less strict matching so the same contract can match multiple similar requests. This is useful for matching contracts with resource identifiers in the path. Any placeholder in the path (like :id in /beers/:id) is considered a resource identifier and will match any alphanumeric combination.
|
6
|
+
|
7
|
+
You can change the default behavior to the behavior that allows placeholders and ignores headers or request parameters by setting the strict_matchers configuration option:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
Pacto.configure do |config|
|
11
|
+
config.strict_matchers = false
|
12
|
+
end
|
13
|
+
```
|
14
|
+
|
15
|
+
Background:
|
16
|
+
Given a file named "contracts/hello_contract.json" with:
|
17
|
+
"""json
|
18
|
+
{
|
19
|
+
"request": {
|
20
|
+
"method": "GET",
|
21
|
+
"path": "/hello/:id",
|
22
|
+
"headers": {
|
23
|
+
"Accept": "application/json"
|
24
|
+
},
|
25
|
+
"params": {}
|
26
|
+
},
|
27
|
+
|
28
|
+
"response": {
|
29
|
+
"status": 200,
|
30
|
+
"headers": { "Content-Type": "application/json" },
|
31
|
+
"body": {
|
32
|
+
"type": "object",
|
33
|
+
"required": true,
|
34
|
+
"properties": {
|
35
|
+
"message": { "type": "string", "required": true, "default": "Hello, world!" }
|
36
|
+
}
|
37
|
+
}
|
38
|
+
}
|
39
|
+
}
|
40
|
+
"""
|
41
|
+
|
42
|
+
Given a file named "requests.rb" with:
|
43
|
+
"""ruby
|
44
|
+
require 'pacto'
|
45
|
+
|
46
|
+
strict = ARGV[0] == "true"
|
47
|
+
puts "Pacto.configuration.strict_matchers = #{strict}"
|
48
|
+
puts
|
49
|
+
|
50
|
+
Pacto.configure do |config|
|
51
|
+
config.strict_matchers = strict
|
52
|
+
end
|
53
|
+
Pacto.load_contracts('contracts', 'http://dummyprovider.com').stub_all
|
54
|
+
|
55
|
+
def response url, headers
|
56
|
+
begin
|
57
|
+
response = Faraday.get(url) do |req|
|
58
|
+
req.headers = headers[:headers]
|
59
|
+
end
|
60
|
+
response.body
|
61
|
+
rescue WebMock::NetConnectNotAllowedError => e
|
62
|
+
e.class
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
print 'Exact: '
|
67
|
+
puts response 'http://dummyprovider.com/hello/:id', headers: {'Accept' => 'application/json' }
|
68
|
+
|
69
|
+
print 'Wrong headers: '
|
70
|
+
puts response 'http://dummyprovider.com/hello/:id', headers: {'Content-Type' => 'application/json' }
|
71
|
+
|
72
|
+
print 'ID placeholder: '
|
73
|
+
puts response 'http://dummyprovider.com/hello/123', headers: {'Accept' => 'application/json' }
|
74
|
+
"""
|
75
|
+
|
76
|
+
Scenario: Default (strict) behavior
|
77
|
+
When I run `bundle exec ruby requests.rb true`
|
78
|
+
Then the output should contain:
|
79
|
+
"""
|
80
|
+
Pacto.configuration.strict_matchers = true
|
81
|
+
|
82
|
+
Exact: {"message":"Hello, world!"}
|
83
|
+
Wrong headers: WebMock::NetConnectNotAllowedError
|
84
|
+
ID placeholder: WebMock::NetConnectNotAllowedError
|
85
|
+
|
86
|
+
"""
|
87
|
+
|
88
|
+
Scenario: Non-strict matching
|
89
|
+
When I run `bundle exec ruby requests.rb false`
|
90
|
+
Then the output should contain:
|
91
|
+
"""
|
92
|
+
Pacto.configuration.strict_matchers = false
|
93
|
+
|
94
|
+
Exact: {"message":"Hello, world!"}
|
95
|
+
Wrong headers: {"message":"Hello, world!"}
|
96
|
+
ID placeholder: {"message":"Hello, world!"}
|
97
|
+
"""
|
@@ -0,0 +1,11 @@
|
|
1
|
+
## Consumer-Driven Contract Recommendations
|
2
|
+
|
3
|
+
If you are using Pacto for Consumer-Driven Contracts, we recommend avoiding the advanced features so you'll test with the strictest Contract possible. We recommend:
|
4
|
+
|
5
|
+
- Do not use templating, let Pacto use the json-generator
|
6
|
+
- Use strict request matching
|
7
|
+
- Use multiple contracts for the same service to capture attributes that are required in some situations but not others
|
8
|
+
|
9
|
+
The host address is intentionally left out of the request specification so that we can validate a contract against any provider.
|
10
|
+
It also reinforces the fact that a contract defines the expectation of a consumer, and not the implementation of any specific provider.
|
11
|
+
|
@@ -0,0 +1,82 @@
|
|
1
|
+
Feature: Existing services journey
|
2
|
+
|
3
|
+
Scenario: Generating the contracts
|
4
|
+
Given I have a Rakefile
|
5
|
+
Given an existing set of services
|
6
|
+
When I execute:
|
7
|
+
"""ruby
|
8
|
+
require 'pacto'
|
9
|
+
|
10
|
+
Pacto.configure do | c |
|
11
|
+
c.contracts_path = 'contracts'
|
12
|
+
end
|
13
|
+
|
14
|
+
Pacto.generate!
|
15
|
+
|
16
|
+
Faraday.get 'http://www.example.com/service1'
|
17
|
+
Faraday.get 'http://www.example.com/service2'
|
18
|
+
"""
|
19
|
+
Then the following files should exist:
|
20
|
+
| contracts/www.example.com/service1.json |
|
21
|
+
| contracts/www.example.com/service2.json |
|
22
|
+
|
23
|
+
@no-clobber
|
24
|
+
Scenario: Ensuring all contracts are valid
|
25
|
+
When I successfully run `bundle exec rake pacto:meta_validate['contracts']`
|
26
|
+
Then the output should contain exactly:
|
27
|
+
"""
|
28
|
+
Validating contracts/www.example.com/service1.json
|
29
|
+
Validating contracts/www.example.com/service2.json
|
30
|
+
All contracts successfully meta-validated
|
31
|
+
|
32
|
+
"""
|
33
|
+
|
34
|
+
@no-clobber
|
35
|
+
Scenario: Stubbing with the contracts
|
36
|
+
Given a file named "test.rb" with:
|
37
|
+
"""ruby
|
38
|
+
require 'pacto'
|
39
|
+
|
40
|
+
Pacto.configure do | c |
|
41
|
+
c.contracts_path = 'contracts'
|
42
|
+
end
|
43
|
+
|
44
|
+
contracts = Pacto.load_contracts('contracts/www.example.com/', 'http://www.example.com')
|
45
|
+
contracts.stub_all
|
46
|
+
|
47
|
+
puts Faraday.get('http://www.example.com/service1').body
|
48
|
+
puts Faraday.get('http://www.example.com/service2').body
|
49
|
+
"""
|
50
|
+
When I successfully run `bundle exec ruby test.rb`
|
51
|
+
Then the output should contain exactly:
|
52
|
+
"""
|
53
|
+
{"thoughtworks":"bar"}
|
54
|
+
{"service2":["bar"]}
|
55
|
+
|
56
|
+
"""
|
57
|
+
|
58
|
+
@no-clobber
|
59
|
+
Scenario: Expecting a change
|
60
|
+
When I make replacements in "contracts/www.example.com/service1.json":
|
61
|
+
| pattern | replacement |
|
62
|
+
| string | integer |
|
63
|
+
When I execute:
|
64
|
+
"""ruby
|
65
|
+
require 'pacto'
|
66
|
+
|
67
|
+
Pacto.stop_generating!
|
68
|
+
|
69
|
+
Pacto.configure do | c |
|
70
|
+
c.contracts_path = 'contracts'
|
71
|
+
end
|
72
|
+
|
73
|
+
Pacto.load_contracts('contracts', 'http://www.example.com').stub_all
|
74
|
+
Pacto.validate!
|
75
|
+
|
76
|
+
Faraday.get 'http://www.example.com/service1'
|
77
|
+
Faraday.get 'http://www.example.com/service2'
|
78
|
+
"""
|
79
|
+
Then the output should contain exactly:
|
80
|
+
"""
|
81
|
+
|
82
|
+
"""
|
@@ -0,0 +1,5 @@
|
|
1
|
+
We know - json-schema can get pretty verbose! It's a powerful tool, but writing the entire Contract by hand for a complex service is a painstaking task. We've created a simple generator to speed this process up. You can invoke it programmatically, or with the provided rake task.
|
2
|
+
|
3
|
+
The basic generate we've bundled with Pacto completes partially defined Contracts - Contracts that have a request defined but no response. We haven't bundled any other generates, but you could use the API to generate from other sources, like existing [VCR](https://github.com/vcr/vcr) cassettes, [apiblueprint](http://apiblueprint.org/), or [WADL](https://wadl.java.net/). If you want some help or ideas, try the [pacto mailing-list](https://groups.google.com/forum/#!forum/pacto-gem).
|
4
|
+
|
5
|
+
Note: Request headers are only recorded if they are in the response's [Vary header](http://www.subbu.org/blog/2007/12/vary-header-for-restful-applications), so make sure your services return a proper Vary for best results!
|
@@ -0,0 +1,28 @@
|
|
1
|
+
@needs_server
|
2
|
+
Feature: Contract Generation
|
3
|
+
|
4
|
+
We know - json-schema can get pretty verbose! It's a powerful tool, but writing the entire Contract by hand for a complex service is a painstaking task. We've created a simple generator to speed this process up. You can invoke it programmatically, or with the provided rake task.
|
5
|
+
|
6
|
+
You just need to create a partial Contract that only describes the request. The generator will then execute the request, and use the response to generate a full Contract.
|
7
|
+
|
8
|
+
Remember, we only record request headers if they are in the response's [Vary header](http://www.subbu.org/blog/2007/12/vary-header-for-restful-applications), so make sure your services return a proper Vary for best results!
|
9
|
+
|
10
|
+
Background:
|
11
|
+
Given a file named "requests/my_contract.json" with:
|
12
|
+
"""
|
13
|
+
{
|
14
|
+
"request": {
|
15
|
+
"method": "GET",
|
16
|
+
"path": "/hello",
|
17
|
+
"headers": {
|
18
|
+
"Accept": "application/json"
|
19
|
+
}
|
20
|
+
},
|
21
|
+
"response": {
|
22
|
+
"status": 200,
|
23
|
+
"body": {
|
24
|
+
"required": true
|
25
|
+
}
|
26
|
+
}
|
27
|
+
}
|
28
|
+
"""
|
@@ -0,0 +1,75 @@
|
|
1
|
+
Given(/^Pacto is configured with:$/) do |string|
|
2
|
+
steps %Q{
|
3
|
+
Given a file named "pacto_config.rb" with:
|
4
|
+
"""ruby
|
5
|
+
#{string}
|
6
|
+
"""
|
7
|
+
}
|
8
|
+
end
|
9
|
+
|
10
|
+
Given(/^I have a Rakefile$/) do
|
11
|
+
steps %Q{
|
12
|
+
Given a file named "Rakefile" with:
|
13
|
+
"""ruby
|
14
|
+
require 'pacto/rake_task'
|
15
|
+
"""
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
When(/^I request "(.*?)"$/) do |url|
|
20
|
+
steps %Q{
|
21
|
+
Given a file named "request.rb" with:
|
22
|
+
"""ruby
|
23
|
+
require 'pacto'
|
24
|
+
require_relative 'pacto_config'
|
25
|
+
require 'faraday'
|
26
|
+
|
27
|
+
resp = Faraday.get('#{url}') do |req|
|
28
|
+
req.headers = { 'Accept' => 'application/json' }
|
29
|
+
end
|
30
|
+
puts resp.body
|
31
|
+
"""
|
32
|
+
When I run `bundle exec ruby request.rb`
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
Given(/^an existing set of services$/) do
|
37
|
+
WebMock.stub_request(:get, 'www.example.com/service1').to_return(:body => {'thoughtworks' => 'pacto' }.to_json)
|
38
|
+
WebMock.stub_request(:post, 'www.example.com/service1').with(:body => 'thoughtworks').to_return(:body => 'pacto')
|
39
|
+
WebMock.stub_request(:get, 'www.example.com/service2').to_return(:body => {'service2' => %w{'thoughtworks', 'pacto'} }.to_json)
|
40
|
+
WebMock.stub_request(:post, 'www.example.com/service2').with(:body => 'thoughtworks').to_return(:body => 'pacto')
|
41
|
+
end
|
42
|
+
|
43
|
+
When(/^I execute:$/) do |script|
|
44
|
+
FileUtils.mkdir_p 'tmp/aruba'
|
45
|
+
Dir.chdir 'tmp/aruba' do
|
46
|
+
begin
|
47
|
+
script = <<-eof
|
48
|
+
require 'stringio'
|
49
|
+
begin $stdout = StringIO.new
|
50
|
+
#{ script }
|
51
|
+
$stdout.string
|
52
|
+
ensure
|
53
|
+
$stdout = STDOUT
|
54
|
+
end
|
55
|
+
eof
|
56
|
+
eval(script) # rubocop:disable Eval
|
57
|
+
# It's just for testing...
|
58
|
+
|
59
|
+
rescue SyntaxError => e
|
60
|
+
puts e
|
61
|
+
puts e.backtrace
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
When(/^I make replacements in "([^"]*)":$/) do |file_name, replacements|
|
67
|
+
Dir.chdir 'tmp/aruba' do
|
68
|
+
content = File.read file_name
|
69
|
+
replacements.rows.each do | pattern, replacement |
|
70
|
+
content.gsub! Regexp.new(pattern), replacement
|
71
|
+
end
|
72
|
+
|
73
|
+
File.open(file_name, 'w') { |file| file.write content }
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
Feature: Templating
|
2
|
+
|
3
|
+
If you want to create more dynamic stubs, you can use Pacto templating. Currently the only supported templating mechanism is to use ERB in the "default" attributes of the json-schema.
|
4
|
+
|
5
|
+
Background:
|
6
|
+
Given Pacto is configured with:
|
7
|
+
"""ruby
|
8
|
+
Pacto.configure do |c|
|
9
|
+
c.register_hook Pacto::Hooks::ERBHook.new
|
10
|
+
end
|
11
|
+
Pacto.load_contracts('contracts', 'http://example.com').stub_all
|
12
|
+
"""
|
13
|
+
Given a file named "contracts/template.json" with:
|
14
|
+
"""json
|
15
|
+
{
|
16
|
+
"request": {
|
17
|
+
"method": "GET",
|
18
|
+
"path": "/hello",
|
19
|
+
"headers": {
|
20
|
+
"Accept": "application/json"
|
21
|
+
},
|
22
|
+
"params": {}
|
23
|
+
},
|
24
|
+
|
25
|
+
"response": {
|
26
|
+
"status": 200,
|
27
|
+
"headers": { "Content-Type": "application/json" },
|
28
|
+
"body": {
|
29
|
+
"type": "object",
|
30
|
+
"required": true,
|
31
|
+
"properties": {
|
32
|
+
"message": { "type": "string", "required": true,
|
33
|
+
"default": "<%= 'Hello, world!'.reverse %>"
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
}
|
38
|
+
}
|
39
|
+
"""
|
40
|
+
|
41
|
+
Scenario: ERB Template
|
42
|
+
When I request "http://example.com/hello"
|
43
|
+
Then the output should contain:
|
44
|
+
"""
|
45
|
+
{"message":"!dlrow ,olleH"}
|
46
|
+
"""
|
data/features/support/env.rb
CHANGED
@@ -1,16 +1,22 @@
|
|
1
1
|
require_relative '../../spec/coveralls_helper'
|
2
2
|
require 'rake'
|
3
|
+
require 'webmock'
|
3
4
|
require 'aruba'
|
4
5
|
require 'aruba/cucumber'
|
5
|
-
require 'aruba/in_process'
|
6
6
|
require 'aruba/jruby' if RUBY_PLATFORM == 'java'
|
7
|
-
|
7
|
+
require_relative '../../spec/pacto/server'
|
8
8
|
|
9
9
|
Before do
|
10
|
+
# Given I successfully run `bundle install` can take a while.
|
10
11
|
@aruba_timeout_seconds = RUBY_PLATFORM == 'java' ? 60 : 10
|
11
12
|
end
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
14
|
+
# Was only going to use for @needs_server, but its easier to leave it running
|
15
|
+
@server = Pacto::Server::Dummy.new 8000, '/hello', '{"message": "Hello World!"}'
|
16
|
+
@server.start
|
17
|
+
|
18
|
+
Around do | scenario, block |
|
19
|
+
Bundler.with_clean_env do
|
20
|
+
block.call
|
21
|
+
end
|
16
22
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
You can use Pacto to do Integration Contract Testing - making sure your service and any test double that simulates the service are similar. If you generate your Contracts from documentation, you can be fairly confident that all three - live services, test doubles, and documentation - are in sync.
|
@@ -0,0 +1,85 @@
|
|
1
|
+
Feature: Validation
|
2
|
+
|
3
|
+
You can validate just the body of the contract. This may be useful if you want to validate a stubbing system that does not stub the fully connection, or if Pacto is currently unable to validate your headers.
|
4
|
+
|
5
|
+
Background:
|
6
|
+
Given a file named "Gemfile" with:
|
7
|
+
"""ruby
|
8
|
+
source 'https://rubygems.org'
|
9
|
+
|
10
|
+
gem 'pacto', :path => '../..'
|
11
|
+
gem 'excon'
|
12
|
+
"""
|
13
|
+
Given a file named "validate.rb" with:
|
14
|
+
"""ruby
|
15
|
+
require 'pacto'
|
16
|
+
require_relative 'my_service'
|
17
|
+
|
18
|
+
contract_list = Pacto.load_contracts('contracts', 'http://example.com')
|
19
|
+
contract_list.stub_all
|
20
|
+
|
21
|
+
contract = contract_list.contracts.first
|
22
|
+
service = MyService.new
|
23
|
+
response = service.hello
|
24
|
+
successful = contract.validate_consumer nil, response, :body_only => true
|
25
|
+
puts "Validated successfully!" if successful
|
26
|
+
"""
|
27
|
+
Given a file named "contracts/template.json" with:
|
28
|
+
"""json
|
29
|
+
{
|
30
|
+
"request": {
|
31
|
+
"method": "GET",
|
32
|
+
"path": "/hello",
|
33
|
+
"headers": {
|
34
|
+
"Accept": "application/json"
|
35
|
+
},
|
36
|
+
"params": {}
|
37
|
+
},
|
38
|
+
|
39
|
+
"response": {
|
40
|
+
"status": 200,
|
41
|
+
"headers": { "Content-Type": "application/json" },
|
42
|
+
"body": {
|
43
|
+
"type": "object",
|
44
|
+
"required": true,
|
45
|
+
"properties": {
|
46
|
+
"message": { "type": "string", "required": true
|
47
|
+
}
|
48
|
+
}
|
49
|
+
}
|
50
|
+
}
|
51
|
+
}
|
52
|
+
"""
|
53
|
+
|
54
|
+
Scenario: Validate the response body only
|
55
|
+
# This should be in the Before block, but https://github.com/cucumber/cucumber/issues/52
|
56
|
+
Given I successfully run `bundle install --local`
|
57
|
+
Given a file named "my_service.rb" with:
|
58
|
+
"""ruby
|
59
|
+
require 'excon'
|
60
|
+
class MyService
|
61
|
+
def response(params={})
|
62
|
+
body = params[:body] || {}
|
63
|
+
status = params[:status] || 200
|
64
|
+
headers = params[:headers] || {}
|
65
|
+
|
66
|
+
response = Excon::Response.new(:body => body, :headers => headers, :status => status)
|
67
|
+
if params.has_key?(:expects) && ![*params[:expects]].include?(response.status)
|
68
|
+
raise(Excon::Errors.status_error(params, response))
|
69
|
+
else response
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def hello
|
74
|
+
body = {
|
75
|
+
'message' => 'Hi!'
|
76
|
+
}
|
77
|
+
response({:body => body})
|
78
|
+
end
|
79
|
+
end
|
80
|
+
"""
|
81
|
+
When I run `bundle exec ruby validate.rb`
|
82
|
+
Then the output should contain:
|
83
|
+
"""
|
84
|
+
Validated successfully!
|
85
|
+
"""
|