WebMock ======= Library for stubbing HTTP requests and setting expectations on HTTP requests in Ruby. Features -------- * 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 Installation ------------ 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 ## Examples ## Stubbing ### Stubbed request based on uri only and with the default response stub_request(:any, "www.example.com") Net::HTTP.get("www.example.com", "/") # ===> Success ### Stubbing requests based on method, uri, body and headers 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 ### Matching custom request headers 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 ### Stubbing with custom response stub_request(:any, "www.example.com").to_return(:body => "abc", :status => 200, :headers => { 'Content-Length' => 3 } ) Net::HTTP.get("www.example.com", '/') # ===> "abc" ### Custom response with body specified as IO object 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" ### Custom response with dynamically evaluated response stub_request(:any, 'www.example.net'). to_return(:body => lambda { |request| request.body }) RestClient.post('www.example.net', 'abc') # ===> "abc\n" ### Request with basic authentication 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 ### Matching uris using regular expressions stub_request(:any, /.*example.*/) Net::HTTP.get('www.example.com', '/') # ===> Success ### Real requests to network can be allowed or disabled 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 ## Setting Expectations ### Setting expectations in Test::Unit 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 ### Expecting real (not stubbed) requests WebMock.allow_net_connect! Net::HTTP.get('www.example.com', '/') # ===> Success assert_requested :get, "http://www.example.com" # ===> Success ### Setting expectations in RSpec This style is borrowed from [fakeweb-matcher](http://github.com/freelancing-god/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") ### Different way of setting expectations in RSpec 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 ## Clearing stubs and request history 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 ## Matching requests 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 ## Precedence of stubs 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" ## Matching URIs 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` ## Matching headers WebMock will match request headers against stubbed request headers in the following situations: 1. 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' }` 2. 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' }` 3. 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" }` ## Bugs and Issues Please submit them here [http://github.com/bblimke/webmock/issues](http://github.com/bblimke/webmock/issues) ## Suggestions If you have any suggestions on how to improve WebMock please send an email to the mailing list [groups.google.com/group/webmock-users](http://groups.google.com/group/webmock-users) I'm particularly interested in how the DSL could be improved. ## Credits Thanks to my fellow [Bambinos](http://new-bamboo.co.uk/) for all the great suggestions! Thank you Fakeweb! This library was inspired by [FakeWeb](fakeweb.rubyforge.org). 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 Copyright 2009 Bartosz Blimke. See LICENSE for details.