Library for stubbing HTTP requests and setting expectations on HTTP requests in Ruby.
- Stubbing HTTP requests at low Net::HTTP level (no need to change tests when you change HTTP lib interface)
- Setting and verifying expectations on HTTP requests
- Matching requests based on method, URI, headers and body
- Smart matching of the same URIs in different representations (also encoded and non encoded forms)
- Smart matching of the same headers in different representations.
- Support for Test::Unit and RSpec (and can be easily extended to other frameworks)
- Support for Net::HTTP and other http libraries based on Net::HTTP (i.e RightHttpConnection, rest-client, HTTParty)
- Support for HTTPClient library (both sync and async requests)
- Easy to extend to other HTTP libraries except Net::HTTP
gem install webmock --source http://gemcutter.org
In your test/test_helper.rb
add these two lines:
require 'webmock/test_unit'
include WebMock
or if you use RSpec add these lines to spec/spec_helper
:
require 'webmock/rspec'
include WebMock
You can also use WebMock without RSpec or Test::Unit support:
require 'webmock'
include WebMock
stub_request(:any, "www.example.com")
Net::HTTP.get("www.example.com", "/") # ===> Success
stub_request(:post, "www.example.com").with(:body => "abc", :headers => { 'Content-Length' => 3 })
uri = URI.parse("http://www.example.com/")
req = Net::HTTP::Post.new(uri.path)
req['Content-Length'] = 3
res = Net::HTTP.start(uri.host, uri.port) {|http|
http.request(req, "abc")
} # ===> Success
stub_request(:any, "www.example.com").
with( :headers=>{ 'Header-Name' => 'Header-Value' } ).to_return(:body => "abc", :status => 200)
uri = URI.parse('http://www.example.com/')
req = Net::HTTP::Post.new(uri.path)
req['Header-Name'] = 'Header-Value'
res = Net::HTTP.start(uri.host, uri.port) {|http|
http.request(req, 'abc')
} # ===> Success
stub_request(:any, "www.example.com").to_return(:body => "abc", :status => 200, :headers => { 'Content-Length' => 3 } )
Net::HTTP.get("www.example.com", '/') # ===> "abc"
File.open('/tmp/response_body.txt', 'w') { |f| f.puts 'abc' }
stub_request(:any, "www.example.com").to_return(:body => File.new('/tmp/response_body.txt'), :status => 200)
Net::HTTP.get('www.example.com', '/') # ===> "abc\n"
stub_request(:any, 'www.example.net').
to_return(:body => lambda { |request| request.body })
RestClient.post('www.example.net', 'abc') # ===> "abc\n"
stub_request(:get, "user:pass@www.example.com")
Net::HTTP.start('www.example.com') {|http|
req = Net::HTTP::Get.new('/')
req.basic_auth 'user', 'pass'
http.request(req)
} # ===> Success
stub_request(:any, /.*example.*/)
Net::HTTP.get('www.example.com', '/') # ===> Success
WebMock.allow_net_connect!
stub_request(:any, "www.example.com").to_return(:body => "abc")
Net::HTTP.get('www.example.com', '/') # ===> "abc"
Net::HTTP.get('www.something.com', '/') # ===> /.+Something.+/
WebMock.disable_net_connect!
Net::HTTP.get('www.something.com', '/') # ===> Failure
require 'webmock/test_unit'
stub_request(:any, "www.example.com")
uri = URI.parse('http://www.example.com/')
req = Net::HTTP::Post.new(uri.path)
req['Content-Length'] = 3
res = Net::HTTP.start(uri.host, uri.port) {|http|
http.request(req, 'abc')
}
assert_requested :post, "http://www.example.com",
:headers => {'Content-Length' => 3}, :body => "abc", :times => 1 # ===> Success
assert_not_requested :get, "http://www.something.com" # ===> Success
WebMock.allow_net_connect!
Net::HTTP.get('www.example.com', '/') # ===> Success
assert_requested :get, "http://www.example.com" # ===> Success
This style is borrowed from fakeweb-matcher
require 'webmock/rspec'
WebMock.should have_requested(:get, "www.example.com").with(:body => "abc", :headers => {'Content-Length' => 3}).twice
WebMock.should_not have_requested(:get, "www.something.com")
request(:post, "www.example.com").with(:body => "abc", :headers => {'Content-Length' => 3}).should have_been_made.once
request(:post, "www.something.com").should have_been_made.times(3)
request(:any, "www.example.com").should_not have_been_made
If you want to reset all current stubs and history of requests use WebMock.reset_webmock
stub_request(:any, "www.example.com")
Net::HTTP.get('www.example.com', '/') # ===> Success
reset_webmock
Net::HTTP.get('www.example.com', '/') # ===> Failure
assert_not_requested :get, "www.example.com" # ===> Success
An executed request matches stubbed request if it passes following criteria:
When request URI matches stubbed request URI string or Regexp pattern
And request method is the same as stubbed request method or stubbed request method is :any
And request body is the same as stubbed request body or stubbed request body is not specified
And request headers match stubbed request headers, or stubbed request headers match a subset of request headers, or stubbed request headers are not specified
Always the last declared stub matching the request will be applied i.e:
stub_request(:get, "www.example.com").to_return(:body => "abc")
stub_request(:get, "www.example.com").to_return(:body => "def")
Net::HTTP.get('www.example.com', '/') # ====> "def"
WebMock will match all different representations of the same URI.
I.e all the following representations of the URI are equal:
"www.example.com"
"www.example.com/"
"www.example.com:80"
"www.example.com:80/"
"http://www.example.com"
"http://www.example.com/"
"http://www.example.com:80"
"http://www.example.com:80/"
The following URIs with basic authentication are also equal for WebMock
"a b:pass@www.example.com"
"a b:pass@www.example.com/"
"a b:pass@www.example.com:80"
"a b:pass@www.example.com:80/"
"http://a b:pass@www.example.com"
"http://a b:pass@www.example.com/"
"http://a b:pass@www.example.com:80"
"http://a b:pass@www.example.com:80/"
"a%20b:pass@www.example.com"
"a%20b:pass@www.example.com/"
"a%20b:pass@www.example.com:80"
"a%20b:pass@www.example.com:80/"
"http://a%20b:pass@www.example.com"
"http://a%20b:pass@www.example.com/"
"http://a%20b:pass@www.example.com:80"
"http://a%20b:pass@www.example.com:80/"
or these
"www.example.com/my path/?a=my param&b=c"
"www.example.com/my%20path/?a=my%20param&b=c"
"www.example.com:80/my path/?a=my param&b=c"
"www.example.com:80/my%20path/?a=my%20param&b=c"
"http://www.example.com/my path/?a=my param&b=c"
"http://www.example.com/my%20path/?a=my%20param&b=c"
"http://www.example.com:80/my path/?a=my param&b=c"
"http://www.example.com:80/my%20path/?a=my%20param&b=c"
If you provide Regexp to match URI, WebMock will try to match it against every valid form of the same url.
I.e /.*my param.*/
will match www.example.com/my%20path
because it is equivalent of www.example.com/my path
WebMock will match request headers against stubbed request headers in the following situations:
-
Stubbed request has headers specified and request headers are the same as stubbed headers
i.e stubbed headers:{ 'Header1' => 'Value1', 'Header1' => 'Value1' }
, requested:{ 'Header1' => 'Value1', 'Header1' => 'Value1' }
-
Stubbed request has headers specified and stubbed request headers are a subset of request headers
i.e stubbed headers:{ 'Header1' => 'Value1' }
, requested:{ 'Header1' => 'Value1', 'Header1' => 'Value1' }
-
Stubbed request has no headers
i.e stubbed headers:nil
, requested:{ 'Header1' => 'Value1', 'Header1' => 'Value1' }
WebMock normalises headers and treats all forms of same headers as equal: i.e the following two sets of headers are equal:
{ "Header1" => "value1", :content_length => 123, :X_CuStOm_hEAder => :value }
{ :header1 => "value1", "Content-Length" => 123, "x-cuSTOM-HeAder" => "value" }
Please submit them here http://github.com/bblimke/webmock/issues
If you have any suggestions on how to improve WebMock please send an email to the mailing list groups.google.com/group/webmock-users
I'm particularly interested in how the DSL could be improved.
Thanks to my fellow Bambinos for all the great suggestions!
Thank you Fakeweb! This library was inspired by FakeWeb. I took couple of solutions from that project. I also copied some code i.e Net:HTTP adapter. Fakeweb architecture unfortunately didn't allow me to extend it easily with the features I needed. I also preferred some things to work differently i.e request stub precedence.
Copyright 2009 Bartosz Blimke. See LICENSE for details.