Library / SDK
bblimke/webmock avatar
bblimke/webmock

WebMock: HTTP stubbing and request expectations for Ruby test suites

Library for stubbing and setting expectations on HTTP requests in Ruby.

4,049 stars580 forksRubyMIT

At a glance

What is it?
WebMock intercepts HTTP at the client library level, so a Ruby test can stub a request without changing code when the HTTP gem changes. It suits teams that need to assert on outbound requests, and it is the wrong tool for replaying recorded cassettes.
Who is it for?
Adopt WebMock when your tests must assert on outbound HTTP calls, and when you want stubbing that survives a swap of HTTP client library. Do not adopt it if you need recorded traffic replayed across runs, or if you only need to silence network access.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 32 days ago.
What is it written in?
Mainly Ruby, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem WebMock solves for Ruby test suites

A Ruby test that calls an external API is slow, flaky and dependent on a service the test author does not control. The usual fix is to replace the HTTP call. Doing that by hand means wrapping every client in an injectable object, and that wrapper has to be rewritten whenever the code switches from Net::HTTP to Faraday or from REST Client to HTTParty.

WebMock takes a different position. The README describes it as a library for "stubbing and setting expectations on HTTP requests in Ruby", and its first listed feature is stubbing "at low http client lib level (no need to change tests when you change HTTP library)". The interception happens below the application code, so a stub written against Net::HTTP keeps working when the production code changes client, provided the new client is one of the supported ones.

The second job is verification. A stub can be declared and then checked after the test body runs, which turns "did this code call the payments API with the right body" into an assertion rather than an inspection of logs. That is the part that distinguishes WebMock from simply disabling the network.

It is aimed at Ruby developers writing automated tests: RSpec, MiniTest, Test::Unit and Cucumber are all listed as supported frameworks, and the README also shows a path for using it outside a test framework entirely.

How interception and request matching work

WebMock hooks into the HTTP client libraries listed in the README, which include Net::HTTP and libraries built on it such as HTTParty and REST Client, plus Curb, Excon, HTTPClient, the http.rb gem, httpx, Manticore, Patron, EM-HTTP-Request, Async::HTTP::Client and Typhoeus. Two of those carry documented limits: Curb support covers only Curb::Easy, and Typhoeus support covers only Typhoeus::Hydra.

Once enabled, a request that matches a registered stub never reaches the network. The stub is defined by a method and a URI pattern, optionally narrowed by body, headers or a block. Matching is deliberately loose in places: the README advertises "smart matching of the same URIs in different representations (also encoded and non encoded forms)" and the same for headers, so an encoded query string and its plain form are treated as the same request.

Body matching is more capable than string equality. A body can be given as a hash, and the README shows the same stub matching a URL-encoded form body, a JSON body and an XML body. There is also hash_including for partial matches, so a stub does not have to enumerate every field the caller sends.

Headers can be matched with regular expressions, and a header that appears multiple times can be matched with an array of values. A block form, stub_request(:post, "www.example.com").with { |request| ... }, hands the request object to your own predicate when the built-in matchers are not enough. URIs can be given as strings, regular expressions, or a lambda.

One behaviour is worth flagging because it breaks stubs written for older versions. Since 2.0.0, WebMock does not match credentials in an Authorization header against credentials in the userinfo part of a URL. A stub written as stub_request(:get, "user:[email protected]") will not match a request that sets basic auth through the header, and the README calls this out explicitly.

Installing WebMock and writing a first stub_request

The gem is published as webmock. The README gives a direct install and a Bundler form for a test group.

bash
  gem install webmock
ruby
  # add to your Gemfile
  group :test do
    gem "webmock"
  end

Integration depends on the framework. For RSpec the README says to require webmock/rspec in spec/spec_helper; for MiniTest, webmock/minitest in test/test_helper; for Test::Unit, webmock/test_unit in test/test_helper.rb; and for Cucumber, a file features/support/webmock.rb containing require 'webmock/cucumber'.

ruby
  # spec/spec_helper.rb
  require 'webmock/rspec'

Outside a test framework, the README shows enabling it by hand. Note that WebMock::API has to be included before WebMock.enable! is called.

ruby
  require 'webmock'
  include WebMock::API

  WebMock.enable!

A first stub can be as small as a method and a host. The README's example stubs any method on www.example.com with a default response, and the following Net::HTTP.get succeeds without touching the network.

ruby
  stub_request(:any, "www.example.com")

  Net::HTTP.get("www.example.com", "/")    # ===> Success

To make the stub meaningful, narrow it. The README matches a POST on the body and a Content-Length header, and returns a chosen body. If the request does not match, the call raises rather than silently hitting the network, which is the behaviour you want in a test suite.

ruby
  stub_request(:post, "www.example.com").
    with(body: "abc", headers: { 'Content-Length' => 3 }).
    to_return(body: "abc")

The repository also documents building from source for the development version: clone the repository, cd into it, and run rake install. The top-level layout includes lib/, spec/, test/ and minitest/ directories along with the gemspec, Rakefile and CHANGELOG.md.

Where WebMock stops being the right tool

WebMock does not record real HTTP traffic. There is no mechanism in the README for capturing a live response and replaying it on a later run, so a test suite built on WebMock depends on stubs that a person wrote. When an external API changes its response shape, the stub keeps returning the old shape and the test keeps passing. That is a real failure mode, and it is the opposite of what a recording-based approach gives you.

Support is per client library, not universal. If your code uses an HTTP client that is not in the list, WebMock cannot intercept it. The two documented partial cases matter in practice: Curb only supports Curb::Easy, and Typhoeus only supports Typhoeus::Hydra. Code that uses Curb::Multi or a Typhoeus interface outside Hydra is outside the covered surface.

Ruby version support is bounded too. The README lists MRI 2.6 through MRI 4.0 and JRuby. A project pinned to an older Ruby than 2.6 is not covered by that list.

The 2.0.0 credential change is a migration hazard rather than a runtime bug. Anyone upgrading from 1.x with stubs that put user:pass in the URL will find those stubs stop matching requests that authenticate via the Authorization header. The README points to CHANGELOG.md for the full set of changes between 1.x and 2.x, and that file is the place to look before a major upgrade.

Finally, WebMock is not a safety net for accidental network access. It intercepts what it is asked to intercept; it is not described as a general sandbox that fails every outbound connection regardless of configuration.

WebMock vs VCR: stubs you write versus cassettes you record

VCR is the comparison that comes up most often, and the difference is in where the response data comes from. With WebMock you declare the request and the response in the test. With VCR you let the first run hit the real service and record the exchange to a cassette file, then replay it on later runs.

That changes the maintenance model. A WebMock stub is code you own and edit; it can express a response that no server would ever return, which is useful for testing error branches such as a 500 or a malformed JSON body. A cassette is a captured artifact; it reflects what the service actually sent at capture time, and it goes stale when the service changes.

The cost runs the other way as well. Cassettes are quick to create for large APIs, while hand-writing stubs for a wide surface is tedious and easy to get subtly wrong. If the goal is broad coverage of a third-party API with minimal authoring effort, WebMock is the slower path.

Both can be used in the same suite. WebMock's interception layer is what VCR-style tools hook into, and the README's feature list is framed around low-level interception rather than recording. If you need both recorded traffic and explicit expectations on specific calls, the two are not mutually exclusive, but the README does not document that combination, so treat it as something to verify in your own setup.

Maintenance, licence and upgrade cost

The repository is not archived, and the last push was on 2026-08-29. Recent releases follow the same date closely: v3.26.4 on 2026-08-29, v3.26.3 on 2026-08-21, and v3.26.2 on 2026-03-18. The gap between v3.26.2 and v3.26.3 shows that releases are not on a fixed schedule; a quarter can pass without one.

The licence is MIT, which permits use in closed-source projects and modification, subject to the usual condition that the copyright notice and permission notice are retained. That is a statement about what the licence text allows, not legal advice; if your organisation has specific redistribution requirements, the LICENSE file at the repository root is the document to check.

Upgrade cost is concentrated in major versions. The README devotes a section to upgrading from 1.x to 2.x and points at CHANGELOG.md for the details, which suggests the maintainers expect breaking changes to be documented there rather than in the README. For minor releases within 3.x, the README does not describe a migration procedure. The practical check before bumping the gem is whether your HTTP client's supported version has moved, particularly if you depend on Curb or Typhoeus.

There is no documented rollback procedure for a WebMock upgrade, and the README does not discuss pinning strategy. The gemspec and Gemfile in the repository are the files that define the dependency constraints if you need to inspect them.

Editorial conclusion

Adopt WebMock when your tests must assert on outbound HTTP calls, and when you want stubbing that survives a swap of HTTP client library. Do not adopt it if you need recorded traffic replayed across runs, or if you only need to silence network access. Before committing, verify that your HTTP client appears in the supported list, and check which version of it WebMock supports, since Curb is limited to Curb::Easy and Typhoeus to Typhoeus::Hydra.

Frequently asked questions

What is WebMock used for in Ruby?

It stubs HTTP requests and sets expectations on them inside Ruby tests. The README describes interception at the HTTP client library level, so tests do not need to change when the HTTP library changes. It supports RSpec, MiniTest, Test::Unit and Cucumber, and can also be enabled outside a test framework.

How do I install WebMock and enable it for RSpec?

Install the webmock gem, either with gem install webmock or by adding gem "webmock" to a test group in your Gemfile. For RSpec, the README says to add require 'webmock/rspec' to spec/spec_helper. MiniTest, Test::Unit and Cucumber use their own require paths.

Which HTTP libraries does WebMock support?

The README lists Async::HTTP::Client, Curb, EM-HTTP-Request, Excon, HTTPClient, the http.rb gem, httpx, Manticore, Net::HTTP and libraries built on it such as HTTParty and REST Client, Patron, and Typhoeus. Curb support covers only Curb::Easy and Typhoeus support covers only Typhoeus::Hydra.

How does WebMock compare with VCR?

WebMock stubs are written by hand in the test, while VCR records real HTTP exchanges and replays them. The README frames WebMock around low-level interception and does not document a recording feature. Hand-written stubs let you return responses a real server would not send, but they go stale silently when an API changes.

Why did my WebMock stub stop matching after upgrading to 2.x?

Since version 2.0.0, WebMock does not match credentials in an Authorization header against credentials in the userinfo part of a URL. A stub written as stub_request(:get, "user:[email protected]") will not match a request that authenticates via the header. The README points to CHANGELOG.md for the full list of 1.x to 2.x changes.

Official sources

  1. bblimke/webmock on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/bblimke-webmock.svg)](https://hysenlabs.com/projects/bblimke-webmock)