webmock 0.7.3 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,9 +1,24 @@
1
+ == 0.8.0
2
+
3
+ * Support for HTTPClient (sync and async requests)
4
+ * Support for dynamic responses. Response body and headers can be now declared as lambda.
5
+ (Thanks to Ivan Vega ( @ivanyv ) for suggesting this feature)
6
+ * Support for stubbing and expecting requests with empty body
7
+ * Executing non-stubbed request leads to failed expectation instead of error
8
+
9
+ === Bug fixes
10
+
11
+ * Basic authentication now works correctly
12
+ * Fixed problem where WebMock didn't call a block with the response when block was provided
13
+ * Fixed problem where uris with single slash were not matching uris without path provided
14
+
1
15
  == 0.7.3
2
16
 
3
17
  * Clarified documentation
4
18
  * Fixed some issues with loading of Webmock classes
5
19
  * Test::Unit and RSpec adapters have to be required separately
6
20
 
21
+
7
22
  == 0.7.2
8
23
 
9
24
  * Added support for matching escaped and non escaped URLs
data/README.md CHANGED
@@ -13,6 +13,7 @@ Features
13
13
  * Smart matching of the same headers in different representations.
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
+ * Support for HTTPClient library (both sync and async requests)
16
17
  * Easy to extend to other HTTP libraries except Net::HTTP
17
18
 
18
19
  Installation
@@ -47,15 +48,15 @@ You can also use WebMock without RSpec or Test::Unit support:
47
48
 
48
49
  ### Stubbed request based on uri only and with the default response
49
50
 
50
- stub_request(:any, "www.google.com")
51
+ stub_request(:any, "www.example.com")
51
52
 
52
- Net::HTTP.get("www.google.com", "/") # ===> Success
53
+ Net::HTTP.get("www.example.com", "/") # ===> Success
53
54
 
54
55
  ### Stubbing requests based on method, uri, body and headers
55
56
 
56
- stub_request(:post, "www.google.com").with(:body => "abc", :headers => { 'Content-Length' => 3 })
57
+ stub_request(:post, "www.example.com").with(:body => "abc", :headers => { 'Content-Length' => 3 })
57
58
 
58
- uri = URI.parse("http://www.google.com/")
59
+ uri = URI.parse("http://www.example.com/")
59
60
  req = Net::HTTP::Post.new(uri.path)
60
61
  req['Content-Length'] = 3
61
62
  res = Net::HTTP.start(uri.host, uri.port) {|http|
@@ -64,10 +65,10 @@ You can also use WebMock without RSpec or Test::Unit support:
64
65
 
65
66
  ### Matching custom request headers
66
67
 
67
- stub_request(:any, "www.google.com").
68
+ stub_request(:any, "www.example.com").
68
69
  with( :headers=>{ 'Header-Name' => 'Header-Value' } ).to_return(:body => "abc", :status => 200)
69
70
 
70
- uri = URI.parse('http://www.google.com/')
71
+ uri = URI.parse('http://www.example.com/')
71
72
  req = Net::HTTP::Post.new(uri.path)
72
73
  req['Header-Name'] = 'Header-Value'
73
74
  res = Net::HTTP.start(uri.host, uri.port) {|http|
@@ -76,37 +77,48 @@ You can also use WebMock without RSpec or Test::Unit support:
76
77
 
77
78
  ### Stubbing with custom response
78
79
 
79
- stub_request(:any, "www.google.com").to_return(:body => "abc", :status => 200, :headers => { 'Content-Length' => 3 } )
80
+ stub_request(:any, "www.example.com").to_return(:body => "abc", :status => 200, :headers => { 'Content-Length' => 3 } )
80
81
 
81
- Net::HTTP.get("www.google.com", '/') # ===> "abc"
82
+ Net::HTTP.get("www.example.com", '/') # ===> "abc"
82
83
 
83
- ### Custom response with body specified as a path to file
84
+ ### Custom response with body specified as IO object
84
85
 
85
86
  File.open('/tmp/response_body.txt', 'w') { |f| f.puts 'abc' }
86
87
 
87
- stub_request(:any, "www.google.com").to_return(:body => "/tmp/response_body.txt", :status => 200)
88
+ stub_request(:any, "www.example.com").to_return(:body => File.new('/tmp/response_body.txt'), :status => 200)
88
89
 
89
- Net::HTTP.get('www.google.com', '/') # ===> "abc\n"
90
+ Net::HTTP.get('www.example.com', '/') # ===> "abc\n"
91
+
92
+ ### Custom response with dynamically evaluated response
93
+
94
+ stub_request(:any, 'www.example.net').
95
+ to_return(:body => lambda { |request| request.body })
96
+
97
+ RestClient.post('www.example.net', 'abc') # ===> "abc\n"
90
98
 
91
99
  ### Request with basic authentication
92
100
 
93
- stub_request(:any, "john:smith@www.google.com")
94
-
95
- Net::HTTP.get(URI.parse('http://john:smith@www.google.com')) # ===> Success
101
+ stub_request(:get, "user:pass@www.example.com")
102
+
103
+ Net::HTTP.start('www.example.com') {|http|
104
+ req = Net::HTTP::Get.new('/')
105
+ req.basic_auth 'user', 'pass'
106
+ http.request(req)
107
+ } # ===> Success
96
108
 
97
109
  ### Matching uris using regular expressions
98
110
 
99
- stub_request(:any, /.*google.*/)
111
+ stub_request(:any, /.*example.*/)
100
112
 
101
- Net::HTTP.get('www.google.com', '/') # ===> Success
113
+ Net::HTTP.get('www.example.com', '/') # ===> Success
102
114
 
103
115
  ### Real requests to network can be allowed or disabled
104
116
 
105
117
  WebMock.allow_net_connect!
106
118
 
107
- stub_request(:any, "www.google.com").to_return(:body => "abc")
119
+ stub_request(:any, "www.example.com").to_return(:body => "abc")
108
120
 
109
- Net::HTTP.get('www.google.com', '/') # ===> "abc"
121
+ Net::HTTP.get('www.example.com', '/') # ===> "abc"
110
122
 
111
123
  Net::HTTP.get('www.something.com', '/') # ===> /.+Something.+/
112
124
 
@@ -120,16 +132,16 @@ You can also use WebMock without RSpec or Test::Unit support:
120
132
  ### Setting expectations in Test::Unit
121
133
  require 'webmock/test_unit'
122
134
 
123
- stub_request(:any, "www.google.com")
135
+ stub_request(:any, "www.example.com")
124
136
 
125
- uri = URI.parse('http://www.google.com/')
137
+ uri = URI.parse('http://www.example.com/')
126
138
  req = Net::HTTP::Post.new(uri.path)
127
139
  req['Content-Length'] = 3
128
140
  res = Net::HTTP.start(uri.host, uri.port) {|http|
129
141
  http.request(req, 'abc')
130
142
  }
131
143
 
132
- assert_requested :post, "http://www.google.com",
144
+ assert_requested :post, "http://www.example.com",
133
145
  :headers => {'Content-Length' => 3}, :body => "abc", :times => 1 # ===> Success
134
146
 
135
147
  assert_not_requested :get, "http://www.something.com" # ===> Success
@@ -138,9 +150,9 @@ You can also use WebMock without RSpec or Test::Unit support:
138
150
 
139
151
  WebMock.allow_net_connect!
140
152
 
141
- Net::HTTP.get('www.google.com', '/') # ===> Success
153
+ Net::HTTP.get('www.example.com', '/') # ===> Success
142
154
 
143
- assert_requested :get, "http://www.google.com" # ===> Success
155
+ assert_requested :get, "http://www.example.com" # ===> Success
144
156
 
145
157
 
146
158
  ### Setting expectations in RSpec
@@ -148,13 +160,13 @@ You can also use WebMock without RSpec or Test::Unit support:
148
160
 
149
161
  require 'webmock/rspec'
150
162
 
151
- WebMock.should have_requested(:get, "www.google.com").with(:body => "abc", :headers => {'Content-Length' => 3}).twice
163
+ WebMock.should have_requested(:get, "www.example.com").with(:body => "abc", :headers => {'Content-Length' => 3}).twice
152
164
 
153
165
  WebMock.should_not have_requested(:get, "www.something.com")
154
166
 
155
167
  ### Different way of setting expectations in RSpec
156
168
 
157
- request(:post, "www.google.com").with(:body => "abc", :headers => {'Content-Length' => 3}).should have_been_made.once
169
+ request(:post, "www.example.com").with(:body => "abc", :headers => {'Content-Length' => 3}).should have_been_made.once
158
170
 
159
171
  request(:post, "www.something.com").should have_been_made.times(3)
160
172
 
@@ -165,34 +177,34 @@ You can also use WebMock without RSpec or Test::Unit support:
165
177
 
166
178
  If you want to reset all current stubs and history of requests use `WebMock.reset_webmock`
167
179
 
168
- stub_request(:any, "www.google.com")
180
+ stub_request(:any, "www.example.com")
169
181
 
170
- Net::HTTP.get('www.google.com', '/') # ===> Success
182
+ Net::HTTP.get('www.example.com', '/') # ===> Success
171
183
 
172
184
  reset_webmock
173
185
 
174
- Net::HTTP.get('www.google.com', '/') # ===> Failure
186
+ Net::HTTP.get('www.example.com', '/') # ===> Failure
175
187
 
176
- assert_not_requested :get, "www.google.com" # ===> Success
188
+ assert_not_requested :get, "www.example.com" # ===> Success
177
189
 
178
190
 
179
191
  ## Matching requests
180
192
 
181
193
  An executed request matches stubbed request if it passes following criteria:
182
194
 
183
- Request URI matches stubbed request URI string or Regexp pattern<br/>
195
+ When request URI matches stubbed request URI string or Regexp pattern<br/>
184
196
  And request method is the same as stubbed request method or stubbed request method is :any<br/>
185
- And request body is the same as stubbed request body or stubbed request body is not set (is nil)<br/>
186
- And request headers match stubbed request headers, or stubbed request headers match a subset of request headers, or stubbed request headers are not set
197
+ And request body is the same as stubbed request body or stubbed request body is not specified<br/>
198
+ And request headers match stubbed request headers, or stubbed request headers match a subset of request headers, or stubbed request headers are not specified
187
199
 
188
200
  ## Precedence of stubs
189
201
 
190
202
  Always the last declared stub matching the request will be applied i.e:
191
203
 
192
- stub_request(:get, "www.google.com").to_return(:body => "abc")
193
- stub_request(:get, "www.google.com").to_return(:body => "def")
204
+ stub_request(:get, "www.example.com").to_return(:body => "abc")
205
+ stub_request(:get, "www.example.com").to_return(:body => "def")
194
206
 
195
- Net::HTTP.get('www.google.com', '/') # ====> "def"
207
+ Net::HTTP.get('www.example.com', '/') # ====> "def"
196
208
 
197
209
  ## Matching URIs
198
210
 
@@ -200,49 +212,49 @@ WebMock will match all different representations of the same URI.
200
212
 
201
213
  I.e all the following representations of the URI are equal:
202
214
 
203
- "www.google.com"
204
- "www.google.com/"
205
- "www.google.com:80"
206
- "www.google.com:80/"
207
- "http://www.google.com"
208
- "http://www.google.com/"
209
- "http://www.google.com:80"
210
- "http://www.google.com:80/"
215
+ "www.example.com"
216
+ "www.example.com/"
217
+ "www.example.com:80"
218
+ "www.example.com:80/"
219
+ "http://www.example.com"
220
+ "http://www.example.com/"
221
+ "http://www.example.com:80"
222
+ "http://www.example.com:80/"
211
223
 
212
224
  The following URIs with basic authentication are also equal for WebMock
213
225
 
214
- "a b:pass@www.google.com"
215
- "a b:pass@www.google.com/"
216
- "a b:pass@www.google.com:80"
217
- "a b:pass@www.google.com:80/"
218
- "http://a b:pass@www.google.com"
219
- "http://a b:pass@www.google.com/"
220
- "http://a b:pass@www.google.com:80"
221
- "http://a b:pass@www.google.com:80/"
222
- "a%20b:pass@www.google.com"
223
- "a%20b:pass@www.google.com/"
224
- "a%20b:pass@www.google.com:80"
225
- "a%20b:pass@www.google.com:80/"
226
- "http://a%20b:pass@www.google.com"
227
- "http://a%20b:pass@www.google.com/"
228
- "http://a%20b:pass@www.google.com:80"
229
- "http://a%20b:pass@www.google.com:80/"
226
+ "a b:pass@www.example.com"
227
+ "a b:pass@www.example.com/"
228
+ "a b:pass@www.example.com:80"
229
+ "a b:pass@www.example.com:80/"
230
+ "http://a b:pass@www.example.com"
231
+ "http://a b:pass@www.example.com/"
232
+ "http://a b:pass@www.example.com:80"
233
+ "http://a b:pass@www.example.com:80/"
234
+ "a%20b:pass@www.example.com"
235
+ "a%20b:pass@www.example.com/"
236
+ "a%20b:pass@www.example.com:80"
237
+ "a%20b:pass@www.example.com:80/"
238
+ "http://a%20b:pass@www.example.com"
239
+ "http://a%20b:pass@www.example.com/"
240
+ "http://a%20b:pass@www.example.com:80"
241
+ "http://a%20b:pass@www.example.com:80/"
230
242
 
231
243
  or these
232
244
 
233
- "www.google.com/big image.jpg/?a=big image&b=c"
234
- "www.google.com/big%20image.jpg/?a=big%20image&b=c"
235
- "www.google.com:80/big image.jpg/?a=big image&b=c"
236
- "www.google.com:80/big%20image.jpg/?a=big%20image&b=c"
237
- "http://www.google.com/big image.jpg/?a=big image&b=c"
238
- "http://www.google.com/big%20image.jpg/?a=big%20image&b=c"
239
- "http://www.google.com:80/big image.jpg/?a=big image&b=c"
240
- "http://www.google.com:80/big%20image.jpg/?a=big%20image&b=c"
245
+ "www.example.com/my path/?a=my param&b=c"
246
+ "www.example.com/my%20path/?a=my%20param&b=c"
247
+ "www.example.com:80/my path/?a=my param&b=c"
248
+ "www.example.com:80/my%20path/?a=my%20param&b=c"
249
+ "http://www.example.com/my path/?a=my param&b=c"
250
+ "http://www.example.com/my%20path/?a=my%20param&b=c"
251
+ "http://www.example.com:80/my path/?a=my param&b=c"
252
+ "http://www.example.com:80/my%20path/?a=my%20param&b=c"
241
253
 
242
254
 
243
255
  If you provide Regexp to match URI, WebMock will try to match it against every valid form of the same url.
244
256
 
245
- I.e `/.*big image.*/` will match `www.google.com/big%20image.jpg` because it is equivalent of `www.google.com/big image.jpg`
257
+ I.e `/.*my param.*/` will match `www.example.com/my%20path` because it is equivalent of `www.example.com/my path`
246
258
 
247
259
 
248
260
  ## Matching headers
data/Rakefile CHANGED
@@ -12,6 +12,7 @@ begin
12
12
  gem.authors = ["Bartosz Blimke"]
13
13
  gem.add_dependency "addressable", ">= 2.1.1"
14
14
  gem.add_development_dependency "rspec", ">= 1.2.9"
15
+ gem.add_development_dependency "httpclient", ">= 2.1.5.2"
15
16
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
16
17
  end
17
18
  Jeweler::GemcutterTasks.new
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.7.3
1
+ 0.8.0
data/lib/webmock.rb CHANGED
@@ -3,6 +3,7 @@ require 'singleton'
3
3
  require 'addressable/uri'
4
4
 
5
5
  require 'webmock/http_lib_adapters/net_http'
6
+ require 'webmock/http_lib_adapters/httpclient'
6
7
 
7
8
  require 'webmock/errors'
8
9
 
@@ -10,6 +11,7 @@ require 'webmock/util/uri'
10
11
  require 'webmock/util/headers'
11
12
  require 'webmock/util/hash_counter'
12
13
 
14
+ require 'webmock/request'
13
15
  require 'webmock/request_profile'
14
16
  require 'webmock/request_signature'
15
17
  require 'webmock/request_stub'
@@ -17,10 +17,7 @@ module WebMock
17
17
  end
18
18
 
19
19
  def with(options)
20
- @request_execution_verifier.request_profile.body =
21
- options[:body] if options.has_key?(:body)
22
- @request_execution_verifier.request_profile.headers =
23
- options[:headers] if options.has_key?(:headers)
20
+ @request_execution_verifier.request_profile.with(options)
24
21
  self
25
22
  end
26
23
 
@@ -0,0 +1,96 @@
1
+ if defined?(HTTPClient)
2
+
3
+ class HTTPClient
4
+
5
+ def do_get_block_with_webmock(req, proxy, conn, &block)
6
+ do_get_with_webmock(req, proxy, conn, false, &block)
7
+ end
8
+
9
+ def do_get_stream_with_webmock(req, proxy, conn, &block)
10
+ do_get_with_webmock(req, proxy, conn, true, &block)
11
+ end
12
+
13
+ def do_get_with_webmock(req, proxy, conn, stream = false, &block)
14
+ request_signature = build_request_signature(req)
15
+
16
+ WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
17
+
18
+ if WebMock.registered_request?(request_signature)
19
+ webmock_response = WebMock.response_for_request(request_signature)
20
+ response = build_httpclient_response(webmock_response, stream, &block)
21
+ conn.push(response)
22
+ elsif WebMock.net_connect_allowed?
23
+ if stream
24
+ do_get_stream_without_webmock(req, proxy, conn, &block)
25
+ else
26
+ do_get_block_without_webmock(req, proxy, conn, &block)
27
+ end
28
+ else
29
+ message = "Real HTTP connections are disabled. Unregistered request: #{request_signature}"
30
+ assertion_failure(message)
31
+ end
32
+ end
33
+
34
+ def do_request_async_with_webmock(method, uri, query, body, extheader)
35
+ req = create_request(method, uri, query, body, extheader)
36
+ request_signature = build_request_signature(req)
37
+
38
+ if WebMock.registered_request?(request_signature) || WebMock.net_connect_allowed?
39
+ do_request_async_without_webmock(method, uri, query, body, extheader)
40
+ else
41
+ message = "Real HTTP connections are disabled. Unregistered request: #{request_signature}"
42
+ assertion_failure(message)
43
+ end
44
+ end
45
+
46
+ alias_method :do_get_block_without_webmock, :do_get_block
47
+ alias_method :do_get_block, :do_get_block_with_webmock
48
+
49
+ alias_method :do_get_stream_without_webmock, :do_get_stream
50
+ alias_method :do_get_stream, :do_get_stream_with_webmock
51
+
52
+ alias_method :do_request_async_without_webmock, :do_request_async
53
+ alias_method :do_request_async, :do_request_async_with_webmock
54
+
55
+ def build_httpclient_response(webmock_response, stream = false, &block)
56
+ body = stream ? StringIO.new(webmock_response.body) : webmock_response.body
57
+ response = HTTP::Message.new_response(body)
58
+ response.header.init_response(webmock_response.status)
59
+
60
+ webmock_response.headers.to_a.each { |name, value| response.header.set(name, value) }
61
+
62
+ webmock_response.raise_error_if_any
63
+
64
+ block.call(nil, body) if block
65
+
66
+ response
67
+ end
68
+ end
69
+
70
+ def build_request_signature(req)
71
+ uri = Addressable::URI.heuristic_parse(req.header.request_uri.to_s)
72
+ uri.query_values = req.header.request_query if req.header.request_query
73
+ uri.port = req.header.request_uri.port
74
+ uri = uri.omit(:userinfo)
75
+
76
+ auth = www_auth.basic_auth
77
+ auth.challenge(req.header.request_uri, nil)
78
+
79
+ headers = Hash[req.header.all]
80
+
81
+ if (auth_cred = auth.get(req)) && auth.scheme == 'Basic'
82
+ userinfo = WebMock::Util::Headers.decode_userinfo_from_header(auth_cred)
83
+ userinfo = WebMock::Util::URI.encode_unsafe_chars_in_userinfo(userinfo)
84
+ headers.reject! {|k,v| k =~ /[Aa]uthorization/ && v =~ /^Basic / } #we added it to url userinfo
85
+ uri.userinfo = userinfo
86
+ end
87
+
88
+ request_signature = WebMock::RequestSignature.new(
89
+ req.header.request_method.downcase.to_sym,
90
+ uri.to_s,
91
+ :body => req.body.content,
92
+ :headers => headers
93
+ )
94
+ end
95
+
96
+ end
@@ -35,11 +35,7 @@ module Net #:nodoc: all
35
35
  when Socket, OpenSSL::SSL::SSLSocket, IO
36
36
  io
37
37
  when String
38
- if !io.include?("\0") && File.exists?(io) && !File.directory?(io)
39
- File.open(io, "r")
40
- else
41
- StringIO.new(io)
42
- end
38
+ StringIO.new(io)
43
39
  end
44
40
  raise "Unable to create local socket" unless @io
45
41
  end
@@ -74,8 +70,9 @@ module Net #:nodoc: all
74
70
 
75
71
  headers = Hash[*request.to_hash.map {|k,v| [k, v.flatten]}.flatten]
76
72
  headers.reject! {|k,v| k =~ /[Aa]ccept/ && v = '*/*'} #removing header added by Net::HTTP
73
+ headers.reject! {|k,v| k =~ /[Aa]uthorization/ && v =~ /^Basic / } #we added it to url userinfo
77
74
 
78
- request_signature = WebMock::RequestSignature.new(method, uri, body, headers)
75
+ request_signature = WebMock::RequestSignature.new(method, uri, :body => body, :headers => headers)
79
76
 
80
77
  WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
81
78
 
@@ -88,7 +85,7 @@ module Net #:nodoc: all
88
85
  request_without_webmock(request, body, &block)
89
86
  else
90
87
  message = "Real HTTP connections are disabled. Unregistered request: #{request_signature}"
91
- raise WebMock::NetConnectNotAllowedError, message
88
+ assertion_failure(message)
92
89
  end
93
90
  end
94
91
  alias_method :request_without_webmock, :request