Rack::VCR

Rack::VCR captures incoming HTTP requests and responses on your Rack application (Rails, Sinatra) and saves them as a VCR fixture in cassettes.

Installation

Add this line to your application's Gemfile:

gem 'rack-vcr'

And then execute:

$ bundle

Or install it yourself as:

$ gem install rack-vcr

Usage

Capturing in Rails

In config/initializers/rack_vcr.rb:

if Rails.env.test?
  Rails.configuration.middleware.insert(0, Rack::VCR)
end

In spec/spec_helper.rb:

VCR.configure do |config|
  config.cassette_library_dir = 'doc/cassettes'
end

RSpec.configure do |config|
  config.around(:each, type: :request) do |example|
    host! "yourapp.hostname"
    name = example.full_description.gsub /[^\w\-]/, '_'
    VCR.use_cassette(name, record: :all) do
      example.run
    end
  end
end

The above example might not work if you were using RSpec 2.x, in which case you might need to write as follows:

RSpec.configure do |config|
  config.around(:each, type: :request) do |ex|
    host! "yourapp.hostname"
    name = example.full_description.gsub /[^\w\-]/, '_'
    VCR.use_cassette(name, record: :all) do
      ex.run
    end
  end
end

Read more about the changes around example on RSpec blog post.

Capturing in Sinatra/Rack

In spec/spec_helper.rb:

require 'rack/test'

VCR.configure do |config|
  config.cassette_library_dir = "vcr_cassettes"
end

describe 'My Web App' do
  include Rack::Test::Methods

  let(:app) {
    Rack::Builder.new do
      use Rack::VCR
      run Sinatra::Application
    end
  }

  it 'runs the request' do
    VCR.use_cassette('hello', record: :all) do
      get "/hello"
    end
    # Now you get vcr_cassettes/hello.yml saved
  end
end

Replaying

Rack::VCR also supports replaying recorded VCR cassettes. It means you can record the HTTP interactions with the real app (on CI), then use the cassette to run a fake/mock API server using Rack::VCR!

To replay cassettes, enable Rack::VCR with :replay option in config.ru or its equivalent.

VCR.configure do |config|
  config.cassette_library_dir = "/path/to/cassettes"
end

Rack::Builder.new do
  use Rack::VCR, replay: true,
    cassette: "test", record: :new_episodes
  run MyApp
end

With the above setting, Rack::VCR will try to locate the cassette named "test" to replay if the request matches with what's recorded, and fall through to the original application if it's not there. It also records the result to the cassette for further requests with the :record option set to :new_episodes.

You can set :record option to :none for example, to only serve what's already recorded in the cassette. The default value for :record option is :new_episodes.

To customize the cassette name in runtime, you can write a custom piece of Rack middleware around Rack::VCR to wrap the application in VCR.use_cassette with its own :record option.

class CassetteLocator
  def initialize(app)
    @app = app
  end

  def call(env)
    cassette = ... # determine cassette from env
    VCR.use_cassette(cassette, record: :none) do
      @app.call(env)
    end
  end
end

Rack::Builder.new do 
  use CassetteLocator
  use Rack::VCR, replay: true
  run MyApp
end

Notes

There's a few similar gems available on Rubygems and GitHub:

  • VCR::Middleware::Rack - Records outgoing HTTP requests inside a Rack application. Quite opposite to what Rack::VCR gem does.
  • rack-recorder - Essentially the same with Rack::VCR, but is very limited in what it does. It doesn't export the captured transaction in VCR compatible format.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/miyagawa/rack-vcr.

Author

Tatsuhiko Miyagawa

License

The gem is available as open source under the terms of the MIT License.