Using WebMock gem in tests and web application services

WebMock is gem for stubbing HTTP requests. You can use it in your tests if you don’t want to hit actual service while testing other functionality of your service (testing in isolation). For example:

require 'webmock'

WebMock.stub_request(:any, "").
  to_return(:body => "some body")

expect(Net::HTTP.get("", "/")).to eq "some body"

It will stub all http verbs (GET, POST, PUT etc.) thanks to :any parameter.

You can also use webmock library for building stubbed versions of your services. This approach is especially useful when services to be called are not implemented yet (maybe by another team) and you still want to start working on your part and finish it on time.

In order to facilitate the creation of mocked service methods, you can use webmock_method gem.

How to use it?

First, create actual service wrapper that works with future API of “not yet developed service”. As an example, we can use publicly available OpenWeatherMap web service.

We will implement call to quote weather for a given city. You have to provide location and units parameters:

# services/open_weather_map_service.rb

require 'net/http'

class OpenWeatherMapService
  attr_reader :url

  def initialize
    @url = ''

  def quote location, units
    quote_url = "#{url}?q=#{location}%20nj&units=#{units}"

    uri = URI.parse(URI.escape(quote_url))

    Net::HTTP.get(uri) # At this moment, service is not developed yet...

Then, create stub/mock for your service:

# stubs/open_weather_map_service.rb

require 'webmock_method'
require 'json'

require 'services/open_weather_map_service.rb'

class OpenWeatherMapService
  extend WebmockMethod

  webmock_method :quote, [:location, :units], lambda { |_| "#{File.dirname(__FILE__)}/stubs/templates/quote_response.json.erb"
    }, /#{WebmockMethod.url}/

webmock_method requires you to provide the following information:

  • method name to be mocked;
  • parameters names for the method (same as in original service);
  • proc/lambda expression for building the response;
  • url to remote service (optional).

You can build responses of arbitrary complexity with your own code or you can use RenderHelper, that comes with this gem. Currently it supports erb and haml renderers only. Here is example of how to build xml response:

  webmock_method :purchase, [:amount, :credit_card],
    lambda { |binding|
      RenderHelper.render :erb, "#{File.dirname(__FILE__)}/templates/purchase_response.xml.erb", binding

It’s possible to tweak your response on the fly:

  webmock_method :purchase, [:amount, :credit_card],
    lambda { |binding|
      RenderHelper.render :erb, "#{File.dirname(__FILE__)}/templates/purchase_response.xml.erb", binding
    } do |parent, _, credit_card|
    if credit_card.card_type == "VISA"
      define_attribute(parent, :success,  true)
      define_attribute(parent, :success,  false)
      define_attribute(parent, :error_message, "Unsupported Credit Card Type")

and then, use newly defined attributes, such as success and error_message inside your template:

<!-- stubs/templates/purchase_response.xml.erb -->
  <Success><%= success %></Success>

  <% unless success %>
    <ErrorMessage><%= error_message %></ErrorMessage>
  <% end %>

url parameter is optional. If you don’t specify it, gem will try to use url attribute defined on your service or you can define url parameter for WebmockMethod:

WebmockMethod.url = ""

And finally, create spec for testing your mocked service:

require "stubs/open_weather_map_service"

describe OpenWeatherMapService do
  describe "#quote" do
    it "gets the quote" do
      result = JSON.parse(subject.quote("plainsboro, nj", "imperial"))

      expect(result['sys']['country']).to eq("United States of America")

If you need to simulate exception raised inside the mocked method, do the following:

  webmock_method :purchase, [:amount, :credit_card],
                 lambda { |binding|
                    # prepare response
                  } do |parent, amount, credit_card|
    define_attribute(parent, :error, create_error(parent, "Negative amount")) if amount < 0


  def self.create_error parent, reason
    define_attribute(parent, :error,


webmock gem code is aware of error variable and will throw this exception, so yo can verify it inside your test:

  it "returns error response if amount is negative" do
    expect{subject.purchase(-1000, valid_credit_card)}.to raise_exception(Exception)

There is another article on same topic from thoughtbot blog. It’s written by Harlow Ward and describes how to create stubbed external services by using fakeweb, vcr and sinatra.

One more project, intereestion in my opinion, is mock 5 gem. It lets you to mock external APIs with simple Sinatra Rack app.