[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-7787":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":16,"stars30d":16,"stars90d":16,"forks30d":16,"starsTrendScore":16,"compositeScore":17,"rankGlobal":10,"rankLanguage":10,"license":18,"archived":19,"fork":19,"defaultBranch":20,"hasWiki":21,"hasPages":19,"topics":22,"createdAt":10,"pushedAt":10,"updatedAt":23,"readmeContent":24,"aiSummary":25,"trendingCount":16,"starSnapshotCount":16,"syncStatus":26,"lastSyncTime":27,"discoverSource":28},7787,"webmock","bblimke\u002Fwebmock","bblimke","Library for stubbing and setting expectations on HTTP requests in Ruby.","",null,"Ruby",4051,577,40,155,0,30.29,"MIT License",false,"master",true,[],"2026-06-12 02:01:44","WebMock\n=======\n[![Gem Version](https:\u002F\u002Fbadge.fury.io\u002Frb\u002Fwebmock.svg)](http:\u002F\u002Fbadge.fury.io\u002Frb\u002Fwebmock)\n[![Build Status](https:\u002F\u002Fgithub.com\u002Fbblimke\u002Fwebmock\u002Factions\u002Fworkflows\u002FCI.yml\u002Fbadge.svg?branch=master)](https:\u002F\u002Fgithub.com\u002Fbblimke\u002Fwebmock\u002Factions)\n[![Code Climate](https:\u002F\u002Fcodeclimate.com\u002Fgithub\u002Fbblimke\u002Fwebmock\u002Fbadges\u002Fgpa.svg)](https:\u002F\u002Fcodeclimate.com\u002Fgithub\u002Fbblimke\u002Fwebmock)\n[![Mentioned in Awesome Ruby](https:\u002F\u002Fawesome.re\u002Fmentioned-badge.svg)](https:\u002F\u002Fgithub.com\u002Fmarkets\u002Fawesome-ruby)\n\nLibrary for stubbing and setting expectations on HTTP requests in Ruby.\n\nFeatures\n--------\n\n* Stubbing HTTP requests at low http client lib level (no need to change tests when you change HTTP library)\n* Setting and verifying expectations on HTTP requests\n* Matching requests based on method, URI, headers and body\n* Smart matching of the same URIs in different representations (also encoded and non encoded forms)\n* Smart matching of the same headers in different representations.\n* Support for Test::Unit\n* Support for RSpec\n* Support for MiniTest\n\nSupported HTTP libraries\n------------------------\n\n* [Async::HTTP::Client](https:\u002F\u002Fgithub.com\u002Fsocketry\u002Fasync-http)\n* [Curb](https:\u002F\u002Fgithub.com\u002Ftaf2\u002Fcurb) (currently only Curb::Easy)\n* [EM-HTTP-Request](https:\u002F\u002Fgithub.com\u002Figrigorik\u002Fem-http-request)\n* [Excon](https:\u002F\u002Fgithub.com\u002Fexcon\u002Fexcon)\n* [HTTPClient](https:\u002F\u002Fgithub.com\u002Fnahi\u002Fhttpclient)\n* [HTTP Gem (also known as http.rb)](https:\u002F\u002Fgithub.com\u002Fhttprb\u002Fhttp)\n* [httpx](https:\u002F\u002Fhoneyryderchuck.gitlab.io\u002Fhttpx\u002Fwiki\u002FWebmock-Adapter)\n* [Manticore](https:\u002F\u002Fgithub.com\u002Fcheald\u002Fmanticore)\n* [Net::HTTP](https:\u002F\u002Fruby-doc.org\u002Fstdlib-2.7.0\u002Flibdoc\u002Fnet\u002Fhttp\u002Frdoc\u002FNet\u002FHTTP.html) and other libraries based on Net::HTTP, e.g.:\n  * [HTTParty](https:\u002F\u002Fgithub.com\u002Fjnunemaker\u002Fhttparty)\n  * [REST Client](https:\u002F\u002Fgithub.com\u002Frest-client\u002Frest-client)\n* [Patron](https:\u002F\u002Fgithub.com\u002Ftoland\u002Fpatron)\n* [Typhoeus](https:\u002F\u002Fgithub.com\u002Ftyphoeus\u002Ftyphoeus) (currently only Typhoeus::Hydra)\n\nSupported Ruby Interpreters\n---------------------------\n* MRI 2.6\n* MRI 2.7\n* MRI 3.0\n* MRI 3.1\n* MRI 3.2\n* MRI 3.3\n* MRI 3.4\n* MRI 4.0\n* JRuby\n\n## Installation\n\n```bash\ngem install webmock\n```\nor alternatively:\n\n```ruby\n# add to your Gemfile\ngroup :test do\n  gem \"webmock\"\nend\n```\n\n### or to install the latest development version from github master\n\n```bash\ngit clone http:\u002F\u002Fgithub.com\u002Fbblimke\u002Fwebmock.git\ncd webmock\nrake install\n```\n\n## Upgrading from v1.x to v2.x\n\nWebMock 2.x has changed somewhat since version 1.x. Changes are listed in [CHANGELOG.md](CHANGELOG.md)\n\n### Cucumber\n\nCreate a file `features\u002Fsupport\u002Fwebmock.rb` with the following contents:\n\n```ruby\nrequire 'webmock\u002Fcucumber'\n```\n\n### MiniTest\n\nAdd the following code to `test\u002Ftest_helper`:\n\n```ruby\nrequire 'webmock\u002Fminitest'\n```\n\n### RSpec\n\nAdd the following code to `spec\u002Fspec_helper`:\n\n```ruby\nrequire 'webmock\u002Frspec'\n```\n\n### Test::Unit\n\nAdd the following code to `test\u002Ftest_helper.rb`\n\n```ruby\nrequire 'webmock\u002Ftest_unit'\n```\n\n### Outside a test framework\n\nYou can also use WebMock outside a test framework:\n\n```ruby\nrequire 'webmock'\ninclude WebMock::API\n\nWebMock.enable!\n```\n\n# Examples\n\n## Stubbing\n\n### Stubbed request based on uri only and with the default response\n\n```ruby\nstub_request(:any, \"www.example.com\")\n\nNet::HTTP.get(\"www.example.com\", \"\u002F\")    # ===> Success\n```\n\n### Stubbing requests based on method, uri, body and headers\n\n```ruby\nstub_request(:post, \"www.example.com\").\n  with(body: \"abc\", headers: { 'Content-Length' => 3 })\n\nuri = URI.parse(\"http:\u002F\u002Fwww.example.com\u002F\")\nreq = Net::HTTP::Post.new(uri.path)\nreq['Content-Length'] = 3\n\nres = Net::HTTP.start(uri.host, uri.port) do |http|\n  http.request(req, \"abc\")\nend    # ===> Success\n```\n\n### Matching request body and headers against regular expressions\n\n```ruby\nstub_request(:post, \"www.example.com\").\n  with(body: \u002Fworld$\u002F, headers: {\"Content-Type\" => \u002Fimage\\\u002F.+\u002F}).\n  to_return(body: \"abc\")\n\nuri = URI.parse('http:\u002F\u002Fwww.example.com\u002F')\nreq = Net::HTTP::Post.new(uri.path)\nreq['Content-Type'] = 'image\u002Fpng'\n\nres = Net::HTTP.start(uri.host, uri.port) do |http|\n  http.request(req, 'hello world')\nend    # ===> Success\n```\n\n### Matching request body against a hash. Body can be URL-Encoded, JSON or XML.\n\n```ruby\nstub_request(:post, \"www.example.com\").\n  with(body: {data: {a: '1', b: 'five'}})\n\nRestClient.post('www.example.com', \"data[a]=1&data[b]=five\",\n  content_type: 'application\u002Fx-www-form-urlencoded')    # ===> Success\n\nRestClient.post('www.example.com', '{\"data\":{\"a\":\"1\",\"b\":\"five\"}}',\n  content_type: 'application\u002Fjson')    # ===> Success\n\nRestClient.post('www.example.com', '\u003Cdata a=\"1\" b=\"five\" \u002F>',\n  content_type: 'application\u002Fxml')    # ===> Success\n```\n\n### Matching request body against partial hash.\n\n```ruby\nstub_request(:post, \"www.example.com\").\n  with(body: hash_including({data: {a: '1', b: 'five'}}))\n\nRestClient.post('www.example.com', \"data[a]=1&data[b]=five&x=1\",\n:content_type => 'application\u002Fx-www-form-urlencoded')    # ===> Success\n```\n\n### Matching custom request headers\n\n```ruby\nstub_request(:any, \"www.example.com\").\n  with(headers:{ 'Header-Name' => 'Header-Value' })\n\nuri = URI.parse('http:\u002F\u002Fwww.example.com\u002F')\nreq = Net::HTTP::Post.new(uri.path)\nreq['Header-Name'] = 'Header-Value'\n\nres = Net::HTTP.start(uri.host, uri.port) do |http|\n  http.request(req, 'abc')\nend    # ===> Success\n```\n\n### Matching multiple headers with the same name\n\n```ruby\nstub_request(:get, 'www.example.com').\n  with(headers: {'Accept' => ['image\u002Fjpeg', 'image\u002Fpng'] })\n\nreq = Net::HTTP::Get.new(\"\u002F\")\nreq['Accept'] = ['image\u002Fpng']\nreq.add_field('Accept', 'image\u002Fjpeg')\nNet::HTTP.start(\"www.example.com\") {|http| http.request(req) }    # ===> Success\n```\n\n### Matching requests against provided block\n\n```ruby\nstub_request(:post, \"www.example.com\").with { |request| request.body == \"abc\" }\nRestClient.post('www.example.com', 'abc')    # ===> Success\n```\n\n### Request with basic authentication header\n\n```ruby\nstub_request(:get, \"www.example.com\").with(basic_auth: ['user', 'pass'])\n# or\n# stub_request(:get, \"www.example.com\").\n#   with(headers: {'Authorization' => \"Basic #{ Base64.strict_encode64('user:pass').chomp}\"})\n\nNet::HTTP.start('www.example.com') do |http|\n  req = Net::HTTP::Get.new('\u002F')\n  req.basic_auth 'user', 'pass'\n  http.request(req)\nend    # ===> Success\n```\n\n##### Important! Since version 2.0.0, WebMock does not match credentials provided in Authorization header and credentials provided in the userinfo of a url. I.e. `stub_request(:get, \"user:pass@www.example.com\")` does not match a request with credentials provided in the Authorization header.\n\n### Request with basic authentication in the url\n\n```ruby\nstub_request(:get, \"user:pass@www.example.com\")\n\nRestClient.get('user:pass@www.example.com')    # ===> Success\n```\n\n### Matching uris using regular expressions\n\n```ruby\nstub_request(:any, \u002Fexample\u002F)\n\nNet::HTTP.get('www.example.com', '\u002F')    # ===> Success\n```\n\n### Matching uris using lambda\n\n```ruby\nstub_request(:any, ->(uri) { true })\n```\n\n### Matching uris using RFC 6570 - Basic Example\n\n```ruby\nuri_template = Addressable::Template.new \"www.example.com\u002F{id}\u002F\"\nstub_request(:any, uri_template)\n\nNet::HTTP.get('www.example.com', '\u002Fwebmock\u002F')    # ===> Success\n```\n\n### Matching uris using RFC 6570 - Advanced Example\n\n```ruby\nuri_template =\n  Addressable::Template.new \"www.example.com\u002Fthing\u002F{id}.json{?x,y,z}{&other*}\"\nstub_request(:any, uri_template)\n\nNet::HTTP.get('www.example.com',\n  '\u002Fthing\u002F5.json?x=1&y=2&z=3&anyParam=4')    # ===> Success\n```\n\n### Matching query params using hash\n\n```ruby\nstub_request(:get, \"www.example.com\").with(query: {\"a\" => [\"b\", \"c\"]})\n\nRestClient.get(\"http:\u002F\u002Fwww.example.com\u002F?a[]=b&a[]=c\")    # ===> Success\n```\n\n### Matching partial query params using hash\n\n```ruby\nstub_request(:get, \"www.example.com\").\n  with(query: hash_including({\"a\" => [\"b\", \"c\"]}))\n\nRestClient.get(\"http:\u002F\u002Fwww.example.com\u002F?a[]=b&a[]=c&x=1\")    # ===> Success\n```\n\n### Matching partial query params using hash_excluding\n\n```ruby\nstub_request(:get, \"www.example.com\").\n  with(query: hash_excluding({\"a\" => \"b\"}))\n\nRestClient.get(\"http:\u002F\u002Fwww.example.com\u002F?a=b\")    # ===> Failure\nRestClient.get(\"http:\u002F\u002Fwww.example.com\u002F?a=c\")    # ===> Success\n```\n\n### Stubbing with custom response\n\n```ruby\nstub_request(:any, \"www.example.com\").\n  to_return(body: \"abc\", status: 200,\n    headers: { 'Content-Length' => 3 })\n\nNet::HTTP.get(\"www.example.com\", '\u002F')    # ===> \"abc\"\n```\n\nSet appropriate Content-Type for HTTParty's `parsed_response`.\n\n```ruby\nstub_request(:any, \"www.example.com\").to_return body: '{}', headers: {content_type: 'application\u002Fjson'}\n```\n\n### Response with body specified as IO object\n\n```ruby\nFile.open('\u002Ftmp\u002Fresponse_body.txt', 'w') { |f| f.puts 'abc' }\n\nstub_request(:any, \"www.example.com\").\n  to_return(body: File.new('\u002Ftmp\u002Fresponse_body.txt'), status: 200)\n\nNet::HTTP.get('www.example.com', '\u002F')    # ===> \"abc\\n\"\n```\n\n### Response with JSON body\n\n```ruby\n\nstub_request(:any, \"www.example.com\").\n  to_return_json(body: {foo: \"bar\"})\n\nNet::HTTP.get('www.example.com', '\u002F') # ===> \"{\\\"foo\\\": \\\"bar\\\"}\"\n```\n\n### Response with custom status message\n\n```ruby\nstub_request(:any, \"www.example.com\").\n  to_return(status: [500, \"Internal Server Error\"])\n\nreq = Net::HTTP::Get.new(\"\u002F\")\nNet::HTTP.start(\"www.example.com\") { |http| http.request(req) }.\n  message    # ===> \"Internal Server Error\"\n```\n\n### Replaying raw responses recorded with `curl -is`\n\n```\ncurl -is www.example.com > \u002Ftmp\u002Fexample_curl_-is_output.txt\n```\n\n```ruby\nraw_response_file = File.new(\"\u002Ftmp\u002Fexample_curl_-is_output.txt\")\n```\n\n   from file\n\n```ruby\nstub_request(:get, \"www.example.com\").to_return(raw_response_file)\n```\n\n   or string\n\n```ruby\nstub_request(:get, \"www.example.com\").to_return(raw_response_file.read)\n```\n\n### Responses dynamically evaluated from block\n\n```ruby\nstub_request(:any, 'www.example.net').\n  to_return { |request| {body: request.body} }\n\nRestClient.post('www.example.net', 'abc')    # ===> \"abc\\n\"\n```\n\n### Responses dynamically evaluated from lambda\n\n```ruby\nstub_request(:any, 'www.example.net').\n  to_return(lambda { |request| {body: request.body} })\n\nRestClient.post('www.example.net', 'abc')    # ===> \"abc\\n\"\n```\n\n### Dynamically evaluated raw responses recorded with `curl -is`\n\n`curl -is www.example.com > \u002Ftmp\u002Fwww.example.com.txt`\n\n```ruby\nstub_request(:get, \"www.example.com\").\n  to_return(lambda { |request| File.new(\"\u002Ftmp\u002F#{request.uri.host.to_s}.txt\") })\n```\n\n### Responses with dynamically evaluated parts\n\n```ruby\nstub_request(:any, 'www.example.net').\n  to_return(body: lambda { |request| request.body })\n\nRestClient.post('www.example.net', 'abc')    # ===> \"abc\\n\"\n```\n\n### Rack responses\n\n```ruby\nclass MyRackApp\n  def self.call(env)\n    [200, {}, [\"Hello\"]]\n  end\nend\n\nstub_request(:get, \"www.example.com\").to_rack(MyRackApp)\n\nRestClient.post('www.example.com')    # ===> \"Hello\"\n```\n\n### Raising errors\n\n#### Exception declared by class\n\n```ruby\nstub_request(:any, 'www.example.net').to_raise(StandardError)\n\nRestClient.post('www.example.net', 'abc')    # ===> StandardError\n```\n\n#### or by exception instance\n\n```ruby\nstub_request(:any, 'www.example.net').to_raise(StandardError.new(\"some error\"))\n```\n\n#### or by string\n\n```ruby\nstub_request(:any, 'www.example.net').to_raise(\"some error\")\n```\n\n### Raising timeout errors\n\n```ruby\nstub_request(:any, 'www.example.net').to_timeout\n\nRestClient.post('www.example.net', 'abc')    # ===> RestClient::RequestTimeout\n```\n\n### Multiple responses for repeated requests\n\n```ruby\nstub_request(:get, \"www.example.com\").\n  to_return({body: \"abc\"}, {body: \"def\"})\nNet::HTTP.get('www.example.com', '\u002F')    # ===> \"abc\\n\"\nNet::HTTP.get('www.example.com', '\u002F')    # ===> \"def\\n\"\n\n#after all responses are used the last response will be returned infinitely\n\nNet::HTTP.get('www.example.com', '\u002F')    # ===> \"def\\n\"\n```\n\n### Multiple responses using chained `to_return()`, `to_raise()` or `to_timeout` declarations\n\n```ruby\nstub_request(:get, \"www.example.com\").\n  to_return({body: \"abc\"}).then.  #then() is just a syntactic sugar\n  to_return({body: \"def\"}).then.\n  to_raise(MyException)\n\nNet::HTTP.get('www.example.com', '\u002F')    # ===> \"abc\\n\"\nNet::HTTP.get('www.example.com', '\u002F')    # ===> \"def\\n\"\nNet::HTTP.get('www.example.com', '\u002F')    # ===> MyException raised\n```\n\n### Specifying number of times given response should be returned\n\n```ruby\nstub_request(:get, \"www.example.com\").\n  to_return({body: \"abc\"}).times(2).then.\n  to_return({body: \"def\"})\n\nNet::HTTP.get('www.example.com', '\u002F')    # ===> \"abc\\n\"\nNet::HTTP.get('www.example.com', '\u002F')    # ===> \"abc\\n\"\nNet::HTTP.get('www.example.com', '\u002F')    # ===> \"def\\n\"\n```\n\n### Removing unused stubs\n\n```ruby\nstub_get = stub_request(:get, \"www.example.com\")\nremove_request_stub(stub_get)\n```\n\n### Real requests to network can be allowed or disabled\n\n```ruby\nWebMock.allow_net_connect!\n\nstub_request(:any, \"www.example.com\").to_return(body: \"abc\")\n\nNet::HTTP.get('www.example.com', '\u002F')    # ===> \"abc\"\n\nNet::HTTP.get('www.something.com', '\u002F')    # ===> \u002F.+Something.+\u002F\n\nWebMock.disable_net_connect!\n\nNet::HTTP.get('www.something.com', '\u002F')    # ===> Failure\n```\n\n### External requests can be disabled while allowing localhost\n\n```ruby\nWebMock.disable_net_connect!(allow_localhost: true)\n\nNet::HTTP.get('www.something.com', '\u002F')    # ===> Failure\n\nNet::HTTP.get('localhost:9887', '\u002F')    # ===> Allowed. Perhaps to Selenium?\n```\n\n### External requests can be disabled while allowing specific requests\n\nAllowed requests can be specified in a number of ways.\n\nWith a `String` specifying a host name:\n\n```ruby\nWebMock.disable_net_connect!(allow: 'www.example.org')\n\nRestClient.get('www.something.com', '\u002F')    # ===> Failure\nRestClient.get('www.example.org', '\u002F')      # ===> Allowed\nRestClient.get('www.example.org:8080', '\u002F') # ===> Allowed\n```\n\nWith a `String` specifying a host name and a port:\n\n```ruby\nWebMock.disable_net_connect!(allow: 'www.example.org:8080')\n\nRestClient.get('www.something.com', '\u002F')    # ===> Failure\nRestClient.get('www.example.org', '\u002F')      # ===> Failure\nRestClient.get('www.example.org:8080', '\u002F') # ===> Allowed\n```\n\nWith a `Regexp` matching the URI:\n\n```ruby\nWebMock.disable_net_connect!(allow: %r{ample\\.org\u002Ffoo})\n\nRestClient.get('www.example.org', '\u002Ffoo\u002Fbar') # ===> Allowed\nRestClient.get('sample.org', '\u002Ffoo')          # ===> Allowed\nRestClient.get('sample.org', '\u002Fbar')          # ===> Failure\n```\n\nWith an object that responds to `#call`, receiving a `URI` object and returning a boolean:\n\n```ruby\ndenylist = ['google.com', 'facebook.com', 'apple.com']\nallowed_sites = lambda{|uri|\n  denylist.none?{|site| uri.host.include?(site) }\n}\nWebMock.disable_net_connect!(allow: allowed_sites)\n\nRestClient.get('www.example.org', '\u002F')  # ===> Allowed\nRestClient.get('www.facebook.com', '\u002F') # ===> Failure\nRestClient.get('apple.com', '\u002F')        # ===> Failure\n```\n\nWith an `Array` of any of the above:\n\n```ruby\nWebMock.disable_net_connect!(allow: [\n  lambda{|uri| uri.host.length % 2 == 0 },\n  \u002Fample.org\u002F,\n  'bbc.co.uk',\n])\n\nRestClient.get('www.example.org', '\u002F') # ===> Allowed\nRestClient.get('bbc.co.uk', '\u002F')       # ===> Allowed\nRestClient.get('bbc.com', '\u002F')         # ===> Allowed\nRestClient.get('www.bbc.com', '\u002F')     # ===> Failure\n```\n\n## Connecting on Net::HTTP.start\n\nHTTP protocol has 3 steps: connect, request and response (or 4 with close). Most Ruby HTTP client libraries\ntreat connect as a part of request step, with the exception of `Net::HTTP` which\nallows opening connection to the server separately to the request, by using `Net::HTTP.start`.\n\nWebMock API was also designed with connect being part of request step, and it only allows stubbing\nrequests, not connections. When `Net::HTTP.start` is called, WebMock doesn't know yet whether\na request is stubbed or not. WebMock by default delays a connection until the request is invoked,\nso when there is no request, `Net::HTTP.start` doesn't do anything.\n**This means that WebMock breaks the Net::HTTP behaviour by default!**\n\nTo workaround this issue, WebMock offers `:net_http_connect_on_start` option,\nwhich can be passed to `WebMock.allow_net_connect!` and `WebMock.disable_net_connect!` methods, i.e.\n\n```ruby\nWebMock.allow_net_connect!(net_http_connect_on_start: true)\n```\n\nThis forces WebMock Net::HTTP adapter to always connect on `Net::HTTP.start`. At the time of connection being made there is no information about the request or URL yet, therefore WebMock is not able to decide whether to stub a request or not and all connections are allowed. To enable connections only to a specific domain (e.g. your test server) use:\n\n```ruby\nWebMock.allow_net_connect!(net_http_connect_on_start: \"www.example.com\")\n```\n\n## Setting Expectations\n\n### Setting expectations in Test::Unit\n\n```ruby\nrequire 'webmock\u002Ftest_unit'\n\nstub_request(:any, \"www.example.com\")\n\nuri = URI.parse('http:\u002F\u002Fwww.example.com\u002F')\nreq = Net::HTTP::Post.new(uri.path)\nreq['Content-Length'] = 3\n\nres = Net::HTTP.start(uri.host, uri.port) do |http|\n  http.request(req, 'abc')\nend\n\nassert_requested :post, \"http:\u002F\u002Fwww.example.com\",\n  headers: {'Content-Length' => 3}, body: \"abc\",\n  times: 1    # ===> Success\n\nassert_not_requested :get, \"http:\u002F\u002Fwww.something.com\"    # ===> Success\n\nassert_requested(:post, \"http:\u002F\u002Fwww.example.com\",\n  times: 1) { |req| req.body == \"abc\" }\n```\n\n### Expecting real (not stubbed) requests\n\n```ruby\nWebMock.allow_net_connect!\n\nNet::HTTP.get('www.example.com', '\u002F')    # ===> Success\n\nassert_requested :get, \"http:\u002F\u002Fwww.example.com\"    # ===> Success\n```\n\n### Setting expectations in Test::Unit on the stub\n\n```ruby\nstub_get = stub_request(:get, \"www.example.com\")\nstub_post = stub_request(:post, \"www.example.com\")\n\nNet::HTTP.get('www.example.com', '\u002F')\n\nassert_requested(stub_get)\nassert_not_requested(stub_post)\n```\n\n\n### Setting expectations in RSpec on `WebMock` module\n This style is borrowed from [fakeweb-matcher](http:\u002F\u002Fgithub.com\u002Fpat\u002Ffakeweb-matcher)\n\n```ruby\nrequire 'webmock\u002Frspec'\n\nexpect(WebMock).to have_requested(:get, \"www.example.com\").\n  with(body: \"abc\", headers: {'Content-Length' => 3}).twice\n\nexpect(WebMock).not_to have_requested(:get, \"www.something.com\")\n\nexpect(WebMock).to have_requested(:post, \"www.example.com\").\n  with { |req| req.body == \"abc\" }\n# Note that the block with `do ... end` instead of curly brackets won't work!\n# Why? See this comment https:\u002F\u002Fgithub.com\u002Fbblimke\u002Fwebmock\u002Fissues\u002F174#issuecomment-34908908\n\nexpect(WebMock).to have_requested(:get, \"www.example.com\").\n  with(query: {\"a\" => [\"b\", \"c\"]})\n\nexpect(WebMock).to have_requested(:get, \"www.example.com\").\n  with(query: hash_including({\"a\" => [\"b\", \"c\"]}))\n\nexpect(WebMock).to have_requested(:get, \"www.example.com\").\n  with(body: {\"a\" => [\"b\", \"c\"]},\n    headers: {'Content-Type' => 'application\u002Fjson'})\n```\n\n### Setting expectations in RSpec with `a_request`\n\n```ruby\nexpect(a_request(:post, \"www.example.com\").\n  with(body: \"abc\", headers: {'Content-Length' => 3})).\n  to have_been_made.once\n\nexpect(a_request(:post, \"www.something.com\")).to have_been_made.times(3)\n\nexpect(a_request(:post, \"www.something.com\")).to have_been_made.at_least_once\n\nexpect(a_request(:post, \"www.something.com\")).\n  to have_been_made.at_least_times(3)\n\nexpect(a_request(:post, \"www.something.com\")).to have_been_made.at_most_twice\n\nexpect(a_request(:post, \"www.something.com\")).to have_been_made.at_most_times(3)\n\nexpect(a_request(:any, \"www.example.com\")).not_to have_been_made\n\nexpect(a_request(:post, \"www.example.com\").with { |req| req.body == \"abc\" }).\n  to have_been_made\n\nexpect(a_request(:get, \"www.example.com\").with(query: {\"a\" => [\"b\", \"c\"]})).\n  to have_been_made\n\nexpect(a_request(:get, \"www.example.com\").\n  with(query: hash_including({\"a\" => [\"b\", \"c\"]}))).to have_been_made\n\nexpect(a_request(:post, \"www.example.com\").\n  with(body: {\"a\" => [\"b\", \"c\"]},\n    headers: {'Content-Type' => 'application\u002Fjson'})).to have_been_made\n```\n\n### Setting expectations in RSpec on the stub\n\n```ruby\nstub = stub_request(:get, \"www.example.com\")\n# ... make requests ...\nexpect(stub).to have_been_requested\n```\n\n## Clearing stubs and request history\n\nIf you want to reset all current stubs and history of requests use `WebMock.reset!`\n\n```ruby\nstub_request(:any, \"www.example.com\")\n\nNet::HTTP.get('www.example.com', '\u002F')    # ===> Success\n\nWebMock.reset!\n\nNet::HTTP.get('www.example.com', '\u002F')    # ===> Failure\n\nassert_not_requested :get, \"www.example.com\"    # ===> Success\n```\n\n## Clearing request counters\n\nIf you want to reset **only** the counters of the executed requests use `WebMock.reset_executed_requests!`\n\n```ruby\nstub  = stub_request(:get, \"www.example.com\")\nstub2 = stub_request(:get, \"www.example2.com\")\n\nNet::HTTP.get('www.example.com', '\u002F')\nNet::HTTP.get('www.example.com', '\u002F')\n\nNet::HTTP.get('www.example2.com', '\u002F')\n\nexpect(stub).to have_been_requested.times(2)\nexpect(stub2).to have_been_requested.times(1)\n\nWebMock.reset_executed_requests!\n\nexpect(stub).not_to have_been_requested\nexpect(stub2).not_to have_been_requested\n```\n\n## Disabling and enabling WebMock or only some http client adapters\n\n```ruby\n# Disable WebMock (all adapters)\nWebMock.disable!\n\n# Disable WebMock for all libs except Net::HTTP\nWebMock.disable!(except: [:net_http])\n\n# Enable WebMock (all adapters)\nWebMock.enable!\n\n# Enable WebMock for all libs except Patron\nWebMock.enable!(except: [:patron])\n```\n\n## Matching requests\n\nAn executed request matches stubbed request if it passes following criteria:\n\n- When request URI matches stubbed request URI string, Regexp pattern or RFC 6570 URI Template\n- And request method is the same as stubbed request method or stubbed request method is :any\n- And request body is the same as stubbed request body or stubbed request body is not specified\n- And request headers match stubbed request headers, or stubbed request headers match a subset of request headers, or stubbed request headers are not specified\n- And request matches provided block or block is not provided\n\n## Precedence of stubs\n\nAlways the last declared stub matching the request will be applied i.e:\n\n```ruby\nstub_request(:get, \"www.example.com\").to_return(body: \"abc\")\nstub_request(:get, \"www.example.com\").to_return(body: \"def\")\n\nNet::HTTP.get('www.example.com', '\u002F')    # ====> \"def\"\n```\n\n## Matching URIs\n\nWebMock will match all different representations of the same URI.\n\nI.e all the following representations of the URI are equal:\n\n```ruby\n\"www.example.com\"\n\"www.example.com\u002F\"\n\"www.example.com:80\"\n\"www.example.com:80\u002F\"\n\"http:\u002F\u002Fwww.example.com\"\n\"http:\u002F\u002Fwww.example.com\u002F\"\n\"http:\u002F\u002Fwww.example.com:80\"\n\"http:\u002F\u002Fwww.example.com:80\u002F\"\n```\n\nThe following URIs with userinfo are also equal for WebMock\n\n```ruby\n\"a b:pass@www.example.com\"\n\"a b:pass@www.example.com\u002F\"\n\"a b:pass@www.example.com:80\"\n\"a b:pass@www.example.com:80\u002F\"\n\"http:\u002F\u002Fa b:pass@www.example.com\"\n\"http:\u002F\u002Fa b:pass@www.example.com\u002F\"\n\"http:\u002F\u002Fa b:pass@www.example.com:80\"\n\"http:\u002F\u002Fa b:pass@www.example.com:80\u002F\"\n\"a%20b:pass@www.example.com\"\n\"a%20b:pass@www.example.com\u002F\"\n\"a%20b:pass@www.example.com:80\"\n\"a%20b:pass@www.example.com:80\u002F\"\n\"http:\u002F\u002Fa%20b:pass@www.example.com\"\n\"http:\u002F\u002Fa%20b:pass@www.example.com\u002F\"\n\"http:\u002F\u002Fa%20b:pass@www.example.com:80\"\n\"http:\u002F\u002Fa%20b:pass@www.example.com:80\u002F\"\n```\n\nor these\n\n```ruby\n\"www.example.com\u002Fmy path\u002F?a=my param&b=c\"\n\"www.example.com\u002Fmy%20path\u002F?a=my%20param&b=c\"\n\"www.example.com:80\u002Fmy path\u002F?a=my param&b=c\"\n\"www.example.com:80\u002Fmy%20path\u002F?a=my%20param&b=c\"\n\"http:\u002F\u002Fwww.example.com\u002Fmy path\u002F?a=my param&b=c\"\n\"http:\u002F\u002Fwww.example.com\u002Fmy%20path\u002F?a=my%20param&b=c\"\n\"http:\u002F\u002Fwww.example.com:80\u002Fmy path\u002F?a=my param&b=c\"\n\"http:\u002F\u002Fwww.example.com:80\u002Fmy%20path\u002F?a=my%20param&b=c\"\n```\n\nIf you provide Regexp to match URI, WebMock will try to match it against every valid form of the same url.\n\nI.e `\u002Fmy path\u002F` will match `www.example.com\u002Fmy%20path` because it is equivalent of `www.example.com\u002Fmy path`\n\n## Matching with URI Templates\n\nIf you use [Addressable::Template](https:\u002F\u002Fgithub.com\u002Fsporkmonger\u002Faddressable#uri-templates) for matching, then WebMock will defer the matching rules to Addressable, which complies with [RFC 6570](http:\u002F\u002Ftools.ietf.org\u002Fhtml\u002Frfc6570).\n\nIf you use any of the WebMock methods for matching query params, then Addressable will be used to match the base URI and WebMock will match the query params.  If you do not, then WebMock will let Addressable match the full URI.\n\n## Matching headers\n\nWebMock will match request headers against stubbed request headers in the following situations:\n\n1. Stubbed request has headers specified and request headers are the same as stubbed headers \u003Cbr\u002F>\ni.e stubbed headers: `{ 'Header1' => 'Value1', 'Header2' => 'Value2' }`, requested: `{ 'Header1' => 'Value1', 'Header2' => 'Value2' }`\n\n2. Stubbed request has headers specified and stubbed request headers are a subset of request headers \u003Cbr\u002F>\ni.e stubbed headers: `{ 'Header1' => 'Value1'  }`, requested: `{ 'Header1' => 'Value1', 'Header2' => 'Value2' }`\n\n3. Stubbed request has no headers \u003Cbr\u002F>\ni.e stubbed headers: `nil`, requested: `{ 'Header1' => 'Value1', 'Header2' => 'Value2' }`\n\nWebMock normalises headers and treats all forms of same headers as equal:\ni.e the following two sets of headers are equal:\n\n`{ \"Header1\" => \"value1\", content_length: 123, X_CuStOm_hEAder: :value }`\n\n`{ header1: \"value1\",  \"Content-Length\" => 123, \"x-cuSTOM-HeAder\" => \"value\" }`\n\n## Recording real requests and responses and replaying them later\n\nTo record your application's real HTTP interactions and replay them later in tests you can use [VCR](https:\u002F\u002Fgithub.com\u002Fvcr\u002Fvcr) with WebMock.\n\n## Request callbacks\n\n#### WebMock can invoke callbacks stubbed or real requests:\n\n```ruby\nWebMock.after_request do |request_signature, response|\n  puts \"Request #{request_signature} was made and #{response} was returned\"\nend\n```\n\n#### invoke callbacks for real requests only and except requests made with Patron\n\n```ruby\nWebMock.after_request(except: [:patron],\n                      real_requests_only: true) do |req_signature, response|\n  puts \"Request #{req_signature} was made and #{response} was returned\"\nend\n```\n\n## Bugs and Issues\n\nPlease submit them here [http:\u002F\u002Fgithub.com\u002Fbblimke\u002Fwebmock\u002Fissues](http:\u002F\u002Fgithub.com\u002Fbblimke\u002Fwebmock\u002Fissues)\n\n## Issue triage [![Open Source Helpers](https:\u002F\u002Fwww.codetriage.com\u002Fbblimke\u002Fwebmock\u002Fbadges\u002Fusers.svg)](https:\u002F\u002Fwww.codetriage.com\u002Fbblimke\u002Fwebmock)\n\nYou can contribute by triaging issues which may include reproducing bug reports or asking for vital information, such as version numbers or reproduction instructions. If you would like to start triaging issues, one easy way to get started is to [subscribe to webmock on CodeTriage](https:\u002F\u002Fwww.codetriage.com\u002Fbblimke\u002Fwebmock).\n\n## Suggestions\n\nIf you have any suggestions on how to improve WebMock please send an email to the mailing list [groups.google.com\u002Fgroup\u002Fwebmock-users](http:\u002F\u002Fgroups.google.com\u002Fgroup\u002Fwebmock-users)\n\nI'm particularly interested in how the DSL could be improved.\n\n## Development\n\nIn order to work on Webmock you first need to fork and clone the repo.\nPlease do any work on a dedicated branch and rebase against master\nbefore sending a pull request.\n\n## Credits\n\nThe initial lines of this project were written during New Bamboo [Hack Day](http:\u002F\u002Fblog.new-bamboo.co.uk\u002F2009\u002F11\u002F13\u002Fhackday-results)\nThanks to my fellow [Bambinos](http:\u002F\u002Fnew-bamboo.co.uk\u002F) for all the great suggestions!\n\nPeople who submitted patches and new features or suggested improvements. Many thanks to these people:\n\n* Ben Pickles\n* Mark Evans\n* Ivan Vega\n* Piotr Usewicz\n* Nick Plante\n* Nick Quaranto\n* Diego E. \"Flameeyes\" Pettenò\n* Niels Meersschaert\n* Mack Earnhardt\n* Arvicco\n* Sergio Gil\n* Jeffrey Jones\n* Tekin Suleyman\n* Tom Ward\n* Nadim Bitar\n* Myron Marston\n* Sam Phillips\n* Jose Angel Cortinas\n* Razic\n* Steve Tooke\n* Nathaniel Bibler\n* Martyn Loughran\n* Muness Alrubaie\n* Charles Li\n* Ryan Bigg\n* Pete Higgins\n* Hans de Graaff\n* Alastair Brunton\n* Sam Stokes\n* Eugene Bolshakov\n* James Conroy-Finn\n* Salvador Fuentes Jr\n* Alex Rothenberg\n* Aidan Feldman\n* Steve Hull\n* Jay Adkisson\n* Zach Dennis\n* Nikita Fedyashev\n* Lin Jen-Shin\n* David Yeu\n* Andreas Garnæs\n* Roman Shterenzon\n* Chris McGrath\n* Stephen Celis\n* Eugene Pimenov\n* Albert Llop\n* Christopher Pickslay\n* Tammer Saleh\n* Nicolas Fouché\n* Joe Van Dyk\n* Mark Abramov\n* Frank Schumacher\n* Dimitrij Denissenko\n* Marnen Laibow-Koser\n* Evgeniy Dolzhenko\n* Nick Recobra\n* Jordan Elver\n* Joe Karayusuf\n* Paul Cortens\n* jugyo\n* aindustries\n* Eric Oestrich\n* erwanlr\n* Ben Bleything\n* Jon Leighton\n* Ryan Schlesinger\n* Julien Boyer\n* Kevin Glowacz\n* Hans Hasselberg\n* Andrew France\n* Jonathan Hyman\n* Rex Feng\n* Pavel Forkert\n* Jordi Massaguer Pla\n* Jake Benilov\n* Tom Beauvais\n* Mokevnin Kirill\n* Alex Grant\n* Lucas Dohmen\n* Bastien Vaucher\n* Joost Baaij\n* Joel Chippindale\n* Murahashi Sanemat Kenichi\n* Tim Kurvers\n* Ilya Vassilevsky\n* gotwalt\n* Leif Bladt\n* Alex Tomlins\n* Mitsutaka Mimura\n* Tomy Kaira\n* Daniel van Hoesel\n* Ian Asaff\n* Ian Lesperance\n* Matthew Horan\n* Dmitry Gutov\n* Florian Dütsch\n* Manuel Meurer\n* Brian D. Burns\n* Riley Strong\n* Tamir Duberstein\n* Stefano Uliari\n* Alex Stupakov\n* Karen Wang\n* Matt Burke\n* Jon Rowe\n* Aleksey V. Zapparov\n* Praveen Arimbrathodiyil\n* Bo Jeanes\n* Matthew Conway\n* Rob Olson\n* Max Lincoln\n* Oleg Gritsenko\n* Hwan-Joon Choi\n* SHIBATA Hiroshi\n* Caleb Thompson\n* Theo Hultberg\n* Pablo Jairala\n* Insoo Buzz Jung\n* Carlos Alonso Pérez\n* trlorenz\n* Alexander Simonov\n* Thorbjørn Hermanse\n* Mark Lorenz\n* tjsousa\n* Tasos Stathopoulos\n* Dan Buettner\n* Sven Riedel\n* Mark Lorenz\n* Dávid Kovács\n* fishermand46\n* Franky Wahl\n* ChaYoung You\n* Simon Russell\n* Steve Mitchell\n* Mattias Putman\n* Zachary Anker\n* Emmanuel Sambo\n* Ramon Tayag\n* Johannes Schlumberger\n* Siôn Le Roux\n* Matt Palmer\n* Zhao Wen\n* Krzysztof Rygielski\n* Magne Land\n* yurivm\n* Mike Knepper\n* Charles Pence\n* Alexey Zapparov\n* Pablo Brasero\n* Cedric Pimenta\n* Michiel Karnebeek\n* Alex Kestner\n* Manfred Stienstra\n* Tim Diggins\n* Gabriel Chaney\n* Chris Griego\n* Taiki Ono\n* Jonathan Schatz\n* Jose Luis Honorato\n* Aaron Kromer\n* Pavel Jurašek\n* Jake Worth\n* Gabe Martin-Dempesy\n* Michael Grosser\n* Aleksei Maridashvili\n* Ville Lautanala\n* Koichi ITO\n* Jordan Harband\n* Tarmo Tänav\n* Joe Marty\n* Chris Thomson\n* Vít Ondruch\n* George Ulmer\n* Christof Koenig\n* Chung-Yi Chi\n* Olexandr Hoshylyk\n* Janko Marohnić\n* Pat Allan\n* Rick Song\n* NARUSE, Yui\n* Piotr Boniecki\n* Olia Kremmyda\n* Michał Matyas\n* Matt Brictson\n* Kenny Ortmann\n* redbar0n\n* Lukas Pokorny\n* Arkadiy Tetelman\n* Kazato Sugimoto\n* Olle Jonsson\n* Pavel Rosický\n* Geremia Taglialatela\n* Koichi Sasada\n* Yusuke Endoh\n* Grey Baker\n* SoonKhen OwYong\n* Pavel Valena\n* Adam Sokolnicki\n* Jeff Felchner\n* Eike Send\n* Claudio Poli\n* Csaba Apagyi\n* Frederick Cheung\n* Fábio D. Batista\n* Andriy Yanko\n* y-yagi\n* Rafael França\n* George Claghorn\n* Alex Junger\n* Orien Madgwick\n* Andrei Sidorov\n* Marco Costa\n* Ryan Davis\n* Brandur\n* Samuel Williams\n* Patrik Ragnarsson\n* Alex Coomans\n* Vesa Laakso\n* John Hawthorn\n* guppy0356\n* Thilo Rusche\n* Andrew Stuntz\n* Lucas Uyezu\n* Bruno Sutic\n* Ryan Kerr\n* Adam Harwood\n* Ben Koshy\n* Jesse Bowes\n* Marek Kasztelnik\n* ce07c3\n* Jun Jiang\n* Oleksiy Kovyrin\n* Matt Larraz\n* Tony Schneider\n* Niklas Hösl\n* Johanna Hartmann\n* Alex Vondrak\n* Will Storey\n* Eduardo Hernandez\n* ojab\n* Giorgio Gambino\n* Timmitry\n* Michael Fairley\n* Ray Zane\n* Go Sueyoshi\n* Cedric Sohrauer\n* Akira Matsuda\n* Mark Spangler\n* Henrik Nyh\n* Yoann Lecuyer\n* Lucas Arnaud\n* Marc Rohloff\n* inkstak\n* Yuki Inoue\n* Brandon Weaver\n* Josh Nichols\n* Ricardo Trindade\n* Earlopain\n* James Brown\n* Kazuhiro NISHIYAMA\n* Étienne Barrié\n* Matt Brown\n* Victor Maslov\n* Gio Lodi\n* Ryan Brooks\n* Jacob Frautschi\n* Christian Schmidt\n* Rodrigo Argumedo\n* Patrick Jaberg\n* Oleg\n* Mikhail Doronin\n* Christoph Rieß\n* Erik Berlin\n\nFor a full list of contributors you can visit the\n[contributors](https:\u002F\u002Fgithub.com\u002Fbblimke\u002Fwebmock\u002Fcontributors) page.\n\n## Background\n\nThank you Fakeweb! This library was inspired by [FakeWeb](https:\u002F\u002Fgithub.com\u002Fchrisk\u002Ffakeweb).\nI imported some solutions from that project to WebMock. I also copied some code i.e Net:HTTP adapter.\nFakeweb architecture unfortunately didn't allow me to extend it easily with the features I needed.\nI also preferred some things to work differently i.e request stub precedence.\n\n## Copyright\n\nCopyright (c) 2009-2010 Bartosz Blimke. See LICENSE for details.\n","WebMock 是一个用于在 Ruby 中模拟和设置 HTTP 请求预期的库。其核心功能包括在低级别的 HTTP 客户端库上进行请求模拟（无需因更换 HTTP 库而修改测试代码）、设定并验证 HTTP 请求预期，以及基于方法、URI、头部和请求体来匹配请求。此外，它还支持多种 HTTP 库和测试框架，如 Test::Unit、RSpec 和 MiniTest，并且智能地处理不同形式的 URI 和头部信息以提高匹配准确性。WebMock 适用于需要对网络请求进行单元测试或集成测试的场景，确保开发过程中能够独立于外部服务进行可靠的测试。",2,"2026-06-11 03:14:21","top_language"]