webmock 0.9.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +47 -2
- data/README.md +68 -6
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/lib/webmock.rb +2 -2
- data/lib/webmock/adapters/rspec.rb +1 -1
- data/lib/webmock/adapters/rspec/matchers.rb +2 -2
- data/lib/webmock/adapters/rspec/{request_profile_matcher.rb → request_pattern_matcher.rb} +5 -5
- data/lib/webmock/adapters/rspec/webmock_matcher.rb +2 -2
- data/lib/webmock/config.rb +2 -1
- data/lib/webmock/http_lib_adapters/httpclient.rb +5 -4
- data/lib/webmock/http_lib_adapters/net_http.rb +5 -3
- data/lib/webmock/http_lib_adapters/patron.rb +82 -0
- data/lib/webmock/request_execution_verifier.rb +8 -8
- data/lib/webmock/request_pattern.rb +130 -0
- data/lib/webmock/request_registry.rb +4 -9
- data/lib/webmock/request_signature.rb +18 -37
- data/lib/webmock/request_stub.rb +17 -6
- data/lib/webmock/response.rb +87 -31
- data/lib/webmock/util/headers.rb +5 -0
- data/lib/webmock/webmock.rb +10 -6
- data/spec/httpclient_spec.rb +0 -1
- data/spec/httpclient_spec_helper.rb +11 -1
- data/spec/net_http_spec.rb +8 -1
- data/spec/net_http_spec_helper.rb +11 -1
- data/spec/patron_spec.rb +83 -0
- data/spec/patron_spec_helper.rb +44 -0
- data/spec/request_execution_verifier_spec.rb +8 -8
- data/spec/request_pattern_spec.rb +243 -0
- data/spec/request_registry_spec.rb +34 -19
- data/spec/request_signature_spec.rb +23 -191
- data/spec/request_stub_spec.rb +32 -11
- data/spec/response_spec.rb +98 -5
- data/spec/spec_helper.rb +8 -16
- data/spec/webmock_spec.rb +154 -49
- data/webmock.gemspec +14 -7
- metadata +21 -7
- data/lib/webmock/request.rb +0 -29
- data/lib/webmock/request_profile.rb +0 -50
- data/spec/request_profile_spec.rb +0 -68
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,50 @@
|
|
1
1
|
#Changelog
|
2
2
|
|
3
|
+
## 1.0.0
|
4
|
+
|
5
|
+
* Added support for [Patron](http://toland.github.com/patron/)
|
6
|
+
|
7
|
+
* Responses dynamically evaluated from block (idea and implementation by Tom Ward)
|
8
|
+
|
9
|
+
stub_request(:any, 'www.example.net').
|
10
|
+
to_return { |request| {:body => request.body} }
|
11
|
+
|
12
|
+
RestClient.post('www.example.net', 'abc') # ===> "abc\n"
|
13
|
+
|
14
|
+
* Responses dynamically evaluated from lambda (idea and implementation by Tom Ward)
|
15
|
+
|
16
|
+
stub_request(:any, 'www.example.net').
|
17
|
+
to_return(lambda { |request| {:body => request.body} })
|
18
|
+
|
19
|
+
RestClient.post('www.example.net', 'abc') # ===> "abc\n"
|
20
|
+
|
21
|
+
* Response with custom status message
|
22
|
+
|
23
|
+
stub_request(:any, "www.example.com").to_return(:status => [500, "Internal Server Error"])
|
24
|
+
|
25
|
+
req = Net::HTTP::Get.new("/")
|
26
|
+
Net::HTTP.start("www.example.com") { |http| http.request(req) }.message # ===> "Internal Server Error"
|
27
|
+
|
28
|
+
* Raising timeout errors (suggested by Jeffrey Jones) (compatibility with Ruby 1.8.6 by Mack Earnhardt)
|
29
|
+
|
30
|
+
stub_request(:any, 'www.example.net').to_timeout
|
31
|
+
|
32
|
+
RestClient.post('www.example.net', 'abc') # ===> RestClient::RequestTimeout
|
33
|
+
|
34
|
+
* External requests can be disabled while allowing localhost (idea and implementation by Mack Earnhardt)
|
35
|
+
|
36
|
+
WebMock.disable_net_connect!(:allow_localhost => true)
|
37
|
+
|
38
|
+
Net::HTTP.get('www.something.com', '/') # ===> Failure
|
39
|
+
|
40
|
+
Net::HTTP.get('localhost:9887', '/') # ===> Allowed. Perhaps to Selenium?
|
41
|
+
|
42
|
+
|
43
|
+
### Bug fixes
|
44
|
+
|
45
|
+
* Fixed issue where Net::HTTP adapter didn't work for requests with body responding to read (reported by Tekin Suleyman)
|
46
|
+
* Fixed issue where request stub with headers declared as nil was matching requests with non empty headers
|
47
|
+
|
3
48
|
## 0.9.1
|
4
49
|
|
5
50
|
* Fixed issue where response status code was not read from raw (curl -is) responses
|
@@ -14,12 +59,12 @@
|
|
14
59
|
#or
|
15
60
|
assert_requested(:post, "www.example.com") { |req| req.body == "abc" }
|
16
61
|
|
17
|
-
* Matching request body against regular expressions
|
62
|
+
* Matching request body against regular expressions (suggested by Ben Pickles)
|
18
63
|
|
19
64
|
stub_request(:post, "www.example.com").with(:body => /^.*world$/).to_return(:body => "abc")
|
20
65
|
RestClient.post('www.example.com', 'hello world') # ===> "abc\n"
|
21
66
|
|
22
|
-
* Matching request headers against regular expressions
|
67
|
+
* Matching request headers against regular expressions (suggested by Ben Pickles)
|
23
68
|
|
24
69
|
stub_request(:post, "www.example.com").with(:headers => {"Content-Type" => /image\/.+/}).to_return(:body => "abc")
|
25
70
|
RestClient.post('www.example.com', '', {'Content-Type' => 'image/png'}) # ===> "abc\n"
|
data/README.md
CHANGED
@@ -14,6 +14,7 @@ Features
|
|
14
14
|
* Support for Test::Unit and RSpec (and can be easily extended to other frameworks)
|
15
15
|
* Support for Net::HTTP and other http libraries based on Net::HTTP (i.e RightHttpConnection, rest-client, HTTParty)
|
16
16
|
* Support for HTTPClient library (both sync and async requests)
|
17
|
+
* Support for Patron library
|
17
18
|
* Easy to extend to other HTTP libraries
|
18
19
|
|
19
20
|
Installation
|
@@ -113,7 +114,7 @@ You can also use WebMock without RSpec or Test::Unit support:
|
|
113
114
|
|
114
115
|
Net::HTTP.get("www.example.com", '/') # ===> "abc"
|
115
116
|
|
116
|
-
###
|
117
|
+
### Response with body specified as IO object
|
117
118
|
|
118
119
|
File.open('/tmp/response_body.txt', 'w') { |f| f.puts 'abc' }
|
119
120
|
|
@@ -121,6 +122,13 @@ You can also use WebMock without RSpec or Test::Unit support:
|
|
121
122
|
|
122
123
|
Net::HTTP.get('www.example.com', '/') # ===> "abc\n"
|
123
124
|
|
125
|
+
### Response with custom status message
|
126
|
+
|
127
|
+
stub_request(:any, "www.example.com").to_return(:status => [500, "Internal Server Error"])
|
128
|
+
|
129
|
+
req = Net::HTTP::Get.new("/")
|
130
|
+
Net::HTTP.start("www.example.com") { |http| http.request(req) }.message # ===> "Internal Server Error"
|
131
|
+
|
124
132
|
### Replaying raw responses recorded with `curl -is`
|
125
133
|
|
126
134
|
`curl -is www.example.com > /tmp/example_curl_-is_output.txt`
|
@@ -134,13 +142,39 @@ You can also use WebMock without RSpec or Test::Unit support:
|
|
134
142
|
|
135
143
|
stub_request(:get, "www.example.com").to_return(raw_response_file.read)
|
136
144
|
|
137
|
-
###
|
138
|
-
|
145
|
+
### Responses dynamically evaluated from block
|
146
|
+
|
147
|
+
stub_request(:any, 'www.example.net').
|
148
|
+
to_return { |request| {:body => request.body} }
|
149
|
+
|
150
|
+
RestClient.post('www.example.net', 'abc') # ===> "abc\n"
|
151
|
+
|
152
|
+
### Responses dynamically evaluated from lambda
|
153
|
+
|
154
|
+
stub_request(:any, 'www.example.net').
|
155
|
+
to_return(lambda { |request| {:body => request.body} })
|
156
|
+
|
157
|
+
RestClient.post('www.example.net', 'abc') # ===> "abc\n"
|
158
|
+
|
159
|
+
### Responses with dynamically evaluated parts
|
160
|
+
|
139
161
|
stub_request(:any, 'www.example.net').
|
140
162
|
to_return(:body => lambda { |request| request.body })
|
141
163
|
|
142
164
|
RestClient.post('www.example.net', 'abc') # ===> "abc\n"
|
143
165
|
|
166
|
+
### Raising errors
|
167
|
+
|
168
|
+
stub_request(:any, 'www.example.net').to_raise(StandardError)
|
169
|
+
|
170
|
+
RestClient.post('www.example.net', 'abc') # ===> StandardError
|
171
|
+
|
172
|
+
### Raising timeout errors
|
173
|
+
|
174
|
+
stub_request(:any, 'www.example.net').to_timeout
|
175
|
+
|
176
|
+
RestClient.post('www.example.net', 'abc') # ===> RestClient::RequestTimeout
|
177
|
+
|
144
178
|
### Multiple responses for repeated requests
|
145
179
|
|
146
180
|
stub_request(:get, "www.example.com").to_return({:body => "abc"}, {:body => "def"})
|
@@ -151,10 +185,10 @@ You can also use WebMock without RSpec or Test::Unit support:
|
|
151
185
|
|
152
186
|
Net::HTTP.get('www.example.com', '/') # ===> "def\n"
|
153
187
|
|
154
|
-
### Multiple responses using chained `to_return()
|
188
|
+
### Multiple responses using chained `to_return()`, `to_raise()` or `to_timeout` declarations
|
155
189
|
|
156
190
|
stub_request(:get, "www.example.com").
|
157
|
-
to_return({:body => "abc"}).then. #then() just
|
191
|
+
to_return({:body => "abc"}).then. #then() is just a syntactic sugar
|
158
192
|
to_return({:body => "def"}).then.
|
159
193
|
to_raise(MyException)
|
160
194
|
Net::HTTP.get('www.example.com', '/') # ===> "abc\n"
|
@@ -186,6 +220,13 @@ You can also use WebMock without RSpec or Test::Unit support:
|
|
186
220
|
|
187
221
|
Net::HTTP.get('www.something.com', '/') # ===> Failure
|
188
222
|
|
223
|
+
### External requests can be disabled while allowing localhost
|
224
|
+
|
225
|
+
WebMock.disable_net_connect!(:allow_localhost => true)
|
226
|
+
|
227
|
+
Net::HTTP.get('www.something.com', '/') # ===> Failure
|
228
|
+
|
229
|
+
Net::HTTP.get('localhost:9887', '/') # ===> Allowed. Perhaps to Selenium?
|
189
230
|
|
190
231
|
## Setting Expectations
|
191
232
|
|
@@ -356,10 +397,31 @@ I'm particularly interested in how the DSL could be improved.
|
|
356
397
|
|
357
398
|
## Credits
|
358
399
|
|
400
|
+
The initial lines of this project were written during New Bamboo [Hack Day](http://blog.new-bamboo.co.uk/2009/11/13/hackday-results)
|
359
401
|
Thanks to my fellow [Bambinos](http://new-bamboo.co.uk/) for all the great suggestions!
|
360
402
|
|
403
|
+
People who submitted patches and new features or suggested improvements. Many thanks to these people:
|
404
|
+
|
405
|
+
* Ben Pickles
|
406
|
+
* Mark Evans
|
407
|
+
* Ivan Vega
|
408
|
+
* Piotr Usewicz
|
409
|
+
* Nick Plante
|
410
|
+
* Nick Quaranto
|
411
|
+
* Diego E. "Flameeyes" Pettenò
|
412
|
+
* Niels Meersschaert
|
413
|
+
* Mack Earnhardt
|
414
|
+
* Arvicco
|
415
|
+
* Sergio Gil
|
416
|
+
* Jeffrey Jones
|
417
|
+
* Tekin Suleyman
|
418
|
+
* Tom Ward
|
419
|
+
* Nadim Bitar
|
420
|
+
|
421
|
+
## Background
|
422
|
+
|
361
423
|
Thank you Fakeweb! This library was inspired by [FakeWeb](fakeweb.rubyforge.org).
|
362
|
-
I
|
424
|
+
I imported some solutions from that project to WebMock. I also copied some code i.e Net:HTTP adapter.
|
363
425
|
Fakeweb architecture unfortunately didn't allow me to extend it easily with the features I needed.
|
364
426
|
I also preferred some things to work differently i.e request stub precedence.
|
365
427
|
|
data/Rakefile
CHANGED
@@ -13,7 +13,7 @@ begin
|
|
13
13
|
gem.add_dependency "addressable", ">= 2.1.1"
|
14
14
|
gem.add_development_dependency "rspec", ">= 1.2.9"
|
15
15
|
gem.add_development_dependency "httpclient", ">= 2.1.5.2"
|
16
|
-
|
16
|
+
gem.add_development_dependency "patron", ">= 0.4.5" unless RUBY_PLATFORM =~ /java/
|
17
17
|
end
|
18
18
|
Jeweler::GemcutterTasks.new
|
19
19
|
rescue LoadError
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
1.0.0
|
data/lib/webmock.rb
CHANGED
@@ -4,6 +4,7 @@ require 'addressable/uri'
|
|
4
4
|
|
5
5
|
require 'webmock/http_lib_adapters/net_http'
|
6
6
|
require 'webmock/http_lib_adapters/httpclient'
|
7
|
+
require 'webmock/http_lib_adapters/patron'
|
7
8
|
|
8
9
|
require 'webmock/errors'
|
9
10
|
|
@@ -11,8 +12,7 @@ require 'webmock/util/uri'
|
|
11
12
|
require 'webmock/util/headers'
|
12
13
|
require 'webmock/util/hash_counter'
|
13
14
|
|
14
|
-
require 'webmock/
|
15
|
-
require 'webmock/request_profile'
|
15
|
+
require 'webmock/request_pattern'
|
16
16
|
require 'webmock/request_signature'
|
17
17
|
require 'webmock/responses_sequence'
|
18
18
|
require 'webmock/request_stub'
|
@@ -1,11 +1,11 @@
|
|
1
1
|
module WebMock
|
2
2
|
module Matchers
|
3
3
|
def have_been_made
|
4
|
-
WebMock::
|
4
|
+
WebMock::RequestPatternMatcher.new
|
5
5
|
end
|
6
6
|
|
7
7
|
def have_not_been_made
|
8
|
-
WebMock::
|
8
|
+
WebMock::RequestPatternMatcher.new.times(0)
|
9
9
|
end
|
10
10
|
|
11
11
|
def have_requested(method, uri)
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module WebMock
|
2
|
-
class
|
2
|
+
class RequestPatternMatcher
|
3
3
|
|
4
4
|
def initialize
|
5
5
|
@request_execution_verifier = RequestExecutionVerifier.new
|
@@ -20,13 +20,13 @@ module WebMock
|
|
20
20
|
self
|
21
21
|
end
|
22
22
|
|
23
|
-
def matches?(
|
24
|
-
@request_execution_verifier.
|
23
|
+
def matches?(request_pattern)
|
24
|
+
@request_execution_verifier.request_pattern = request_pattern
|
25
25
|
@request_execution_verifier.matches?
|
26
26
|
end
|
27
27
|
|
28
|
-
def does_not_match?(
|
29
|
-
@request_execution_verifier.
|
28
|
+
def does_not_match?(request_pattern)
|
29
|
+
@request_execution_verifier.request_pattern = request_pattern
|
30
30
|
@request_execution_verifier.does_not_match?
|
31
31
|
end
|
32
32
|
|
@@ -3,7 +3,7 @@ module WebMock
|
|
3
3
|
|
4
4
|
def initialize(method, uri)
|
5
5
|
@request_execution_verifier = RequestExecutionVerifier.new
|
6
|
-
@request_execution_verifier.
|
6
|
+
@request_execution_verifier.request_pattern = RequestPattern.new(method, uri)
|
7
7
|
end
|
8
8
|
|
9
9
|
def once
|
@@ -17,7 +17,7 @@ module WebMock
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def with(options = {}, &block)
|
20
|
-
@request_execution_verifier.
|
20
|
+
@request_execution_verifier.request_pattern.with(options, &block)
|
21
21
|
self
|
22
22
|
end
|
23
23
|
|
data/lib/webmock/config.rb
CHANGED
@@ -19,7 +19,7 @@ if defined?(HTTPClient)
|
|
19
19
|
webmock_response = WebMock.response_for_request(request_signature)
|
20
20
|
response = build_httpclient_response(webmock_response, stream, &block)
|
21
21
|
conn.push(response)
|
22
|
-
elsif WebMock.net_connect_allowed?
|
22
|
+
elsif WebMock.net_connect_allowed?(req.header.request_uri)
|
23
23
|
if stream
|
24
24
|
do_get_stream_without_webmock(req, proxy, conn, &block)
|
25
25
|
else
|
@@ -35,7 +35,7 @@ if defined?(HTTPClient)
|
|
35
35
|
req = create_request(method, uri, query, body, extheader)
|
36
36
|
request_signature = build_request_signature(req)
|
37
37
|
|
38
|
-
if WebMock.registered_request?(request_signature) || WebMock.net_connect_allowed?
|
38
|
+
if WebMock.registered_request?(request_signature) || WebMock.net_connect_allowed?(uri)
|
39
39
|
do_request_async_without_webmock(method, uri, query, body, extheader)
|
40
40
|
else
|
41
41
|
message = "Real HTTP connections are disabled. Unregistered request: #{request_signature}"
|
@@ -55,10 +55,11 @@ if defined?(HTTPClient)
|
|
55
55
|
def build_httpclient_response(webmock_response, stream = false, &block)
|
56
56
|
body = stream ? StringIO.new(webmock_response.body) : webmock_response.body
|
57
57
|
response = HTTP::Message.new_response(body)
|
58
|
-
response.header.init_response(webmock_response.status)
|
59
|
-
|
58
|
+
response.header.init_response(webmock_response.status[0])
|
59
|
+
response.reason=webmock_response.status[1]
|
60
60
|
webmock_response.headers.to_a.each { |name, value| response.header.set(name, value) }
|
61
61
|
|
62
|
+
raise HTTPClient::TimeoutError if webmock_response.should_timeout
|
62
63
|
webmock_response.raise_error_if_any
|
63
64
|
|
64
65
|
block.call(nil, body) if block
|
@@ -91,9 +91,9 @@ module Net #:nodoc: all
|
|
91
91
|
@socket = Net::HTTP.socket_type.new
|
92
92
|
webmock_response = WebMock.response_for_request(request_signature)
|
93
93
|
build_net_http_response(webmock_response, &block)
|
94
|
-
elsif WebMock.net_connect_allowed?
|
94
|
+
elsif WebMock.net_connect_allowed?(uri)
|
95
95
|
connect_without_webmock
|
96
|
-
request_without_webmock(request,
|
96
|
+
request_without_webmock(request, nil, &block)
|
97
97
|
else
|
98
98
|
message = "Real HTTP connections are disabled. Unregistered request: #{request_signature}"
|
99
99
|
WebMock.assertion_failure(message)
|
@@ -114,7 +114,7 @@ module Net #:nodoc: all
|
|
114
114
|
alias_method :connect, :connect_with_webmock
|
115
115
|
|
116
116
|
def build_net_http_response(webmock_response, &block)
|
117
|
-
response = Net::HTTPResponse.send(:response_class, webmock_response.status.to_s).new("1.0", webmock_response.status.to_s,
|
117
|
+
response = Net::HTTPResponse.send(:response_class, webmock_response.status[0].to_s).new("1.0", webmock_response.status[0].to_s, webmock_response.status[1])
|
118
118
|
response.instance_variable_set(:@body, webmock_response.body)
|
119
119
|
webmock_response.headers.to_a.each { |name, value| response[name] = value }
|
120
120
|
|
@@ -122,6 +122,8 @@ module Net #:nodoc: all
|
|
122
122
|
|
123
123
|
response.extend StubResponse
|
124
124
|
|
125
|
+
raise Timeout::Error, "execution expired" if webmock_response.should_timeout
|
126
|
+
|
125
127
|
webmock_response.raise_error_if_any
|
126
128
|
|
127
129
|
yield response if block_given?
|
@@ -0,0 +1,82 @@
|
|
1
|
+
if defined?(Patron)
|
2
|
+
|
3
|
+
module Patron
|
4
|
+
class Session
|
5
|
+
|
6
|
+
def handle_request_with_webmock(req)
|
7
|
+
request_signature = build_request_signature(req)
|
8
|
+
|
9
|
+
WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
|
10
|
+
|
11
|
+
if WebMock.registered_request?(request_signature)
|
12
|
+
webmock_response = WebMock.response_for_request(request_signature)
|
13
|
+
handle_file_name(req, webmock_response)
|
14
|
+
build_patron_response(webmock_response)
|
15
|
+
elsif WebMock.net_connect_allowed?(req.url)
|
16
|
+
handle_request_without_webmock(req)
|
17
|
+
else
|
18
|
+
message = "Real HTTP connections are disabled. Unregistered request: #{request_signature}"
|
19
|
+
WebMock.assertion_failure(message)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
alias_method :handle_request_without_webmock, :handle_request
|
24
|
+
alias_method :handle_request, :handle_request_with_webmock
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
def handle_file_name(req, webmock_response)
|
29
|
+
if req.action == :get && req.file_name
|
30
|
+
begin
|
31
|
+
File.open(req.file_name, "w") do |f|
|
32
|
+
f.write webmock_response.body
|
33
|
+
end
|
34
|
+
rescue Errno::EACCES
|
35
|
+
raise ArgumentError.new("Unable to open specified file.")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def build_request_signature(req)
|
41
|
+
uri = Addressable::URI.heuristic_parse(req.url)
|
42
|
+
uri.path = uri.normalized_path.gsub("[^:]//","/")
|
43
|
+
uri.user = req.username
|
44
|
+
uri.password = req.password
|
45
|
+
|
46
|
+
if [:put, :post].include?(req.action)
|
47
|
+
if req.file_name
|
48
|
+
if !File.exist?(req.file_name) || !File.readable?(req.file_name)
|
49
|
+
raise ArgumentError.new("Unable to open specified file.")
|
50
|
+
end
|
51
|
+
request_body = File.read(req.file_name)
|
52
|
+
elsif req.upload_data
|
53
|
+
request_body = req.upload_data
|
54
|
+
else
|
55
|
+
raise ArgumentError.new("Must provide either data or a filename when doing a PUT or POST")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
request_signature = WebMock::RequestSignature.new(
|
60
|
+
req.action,
|
61
|
+
uri.to_s,
|
62
|
+
:body => request_body,
|
63
|
+
:headers => req.headers
|
64
|
+
)
|
65
|
+
request_signature
|
66
|
+
end
|
67
|
+
|
68
|
+
def build_patron_response(webmock_response)
|
69
|
+
raise Patron::TimeoutError if webmock_response.should_timeout
|
70
|
+
webmock_response.raise_error_if_any
|
71
|
+
res = Patron::Response.new
|
72
|
+
res.instance_variable_set(:@body, webmock_response.body)
|
73
|
+
res.instance_variable_set(:@status, webmock_response.status[0])
|
74
|
+
res.instance_variable_set(:@status_line, webmock_response.status[1])
|
75
|
+
res.instance_variable_set(:@headers, webmock_response.headers)
|
76
|
+
res
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|