Casettes

Cassette format

VCR Cassettes are files that contain all of the information
about the requests and corresponding responses in a
human-readable/editable format. A cassette contains an array
of HTTP interactions, each of which has the following:

  • request
    • method
    • uri
    • body
      • encoding
      • string
    • headers
  • response
    • status
      • code
      • message
    • headers
    • body
      • encoding
      • string
    • http version

By default, VCR uses YAML to serialize this data. You can configure
VCR to use a different serializer, either on a cassette-by-cassette
basis, or as a default for all cassettes if you use the default_cassette_options.

VCR supports the following serializers out of the box:

  • :yaml–Uses ruby’s standard library YAML. This may use psych or syck, depending on your ruby installation.
  • :syck–Uses syck (the ruby 1.8 YAML engine). This is useful when using VCR on a project that must run in environments where psych is not available (such as on ruby 1.8), to ensure that syck is always used.
  • :psych–Uses psych (the new ruby 1.9 YAML engine). This is useful when you want to ensure that psych is always used.
  • :json–Uses Ruby’s standard library to serialize the cassette data as JSON.
  • :compressed–Wraps the default YAML serializer with Zlib, writing compressed cassettes to disk.

You can also register a custom serializer using:

 VCR.configure do |config|
   config.cassette_serializers[:my_custom_serializer] = my_serializer
 end

Your serializer must implement the following methods:

  • file_extension
  • serialize(hash)
  • deserialize(string)

Scenarios

  1. Request/Response data is saved to disk as YAML by default
  2. Request/Response data can be saved as JSON
  3. Request/Response data can be saved as compressed YAML
  4. Request/Response data can be saved using a custom serializer

Request/Response data is saved to disk as YAML by defaultGivena file named “cassette_yaml.rb” with:

include_http_adapter_for("http_lib")

if ARGV.any?
  $server = start_sinatra_app do
    get('/:path') { ARGV[0] + ' ' + params[:path] }
  end
end

require 'vcr'

VCR.configure do |c|
  configuration
  c.cassette_library_dir = 'cassettes'
  c.before_record do |i|
    i.request.uri.sub!(/:\d+/, ':7777')
  end
end

VCR.use_cassette('example') do
  make_http_request(:get, "http://localhost:#{$server.port}/foo", nil, 'Accept-Encoding' => 'identity')
  make_http_request(:get, "http://localhost:#{$server.port}/bar", nil, 'Accept-Encoding' => 'identity')
end

AndI am configuring with “configuration” using “http_lib“WhenI successfully run ruby cassette_yaml.rb 'Hello'Thenthe file “cassettes/example.yml” should contain YAML like:

---
http_interactions:
- request:
    method: get
    uri: http://localhost:7777/foo
    body:
      encoding: UTF-8
      string: ""
    headers:
      Accept-Encoding:
      - identity
  response:
    status:
      code: 200
      message: OK
    headers:
      Content-Type:
      - text/html;charset=utf-8
      Content-Length:
      - "9"
    body:
      encoding: UTF-8
      string: Hello foo
    http_version: "1.1"
  recorded_at: Tue, 01 Nov 2011 04:58:44 GMT
- request:
    method: get
    uri: http://localhost:7777/bar
    body:
      encoding: UTF-8
      string: ""
    headers:
      Accept-Encoding:
      - identity
  response:
    status:
      code: 200
      message: OK
    headers:
      Content-Type:
      - text/html;charset=utf-8
      Content-Length:
      - "9"
    body:
      encoding: UTF-8
      string: Hello bar
    http_version: "1.1"
  recorded_at: Tue, 01 Nov 2011 04:58:44 GMT
recorded_with: VCR 2.0.0
configurationhttp_lib
c.hook_into :webmocknet/http
c.hook_into :webmockhttpclient
c.hook_into :webmockpatron
c.hook_into :webmockcurb
c.hook_into :webmockem-http-request
c.hook_into :webmocktyphoeus
c.hook_into :typhoeustyphoeus
c.hook_into :exconexcon
c.hook_into :faradayfaraday (w/ net_http)

Request/Response data can be saved as JSONGivena file named “cassette_json.rb” with:

include_http_adapter_for("net/http")

$server = start_sinatra_app do
  get('/:path') { ARGV[0] + ' ' + params[:path] }
end

require 'vcr'

VCR.configure do |c|
  c.hook_into :webmock
  c.cassette_library_dir = 'cassettes'
  c.before_record do |i|
    i.request.uri.sub!(/:\d+/, ':7777')
  end
  c.default_cassette_options = {
    :match_requests_on => [:method, :host, :path]
  }
end

VCR.use_cassette('example', :serialize_with => :json) do
  puts response_body_for(:get, "http://localhost:#{$server.port}/foo", nil, 'Accept-Encoding' => 'identity')
  puts response_body_for(:get, "http://localhost:#{$server.port}/bar", nil, 'Accept-Encoding' => 'identity')
end

WhenI run ruby cassette_json.rb 'Hello'Thenthe file “cassettes/example.json” should contain JSON like:

{
  "http_interactions": [
    {
      "response": {
        "body": {
          "encoding": "UTF-8",
          "string": "Hello foo"
        },
        "http_version": null,
        "status": { "code": 200, "message": "OK" },
        "headers": {
          "Date": [ "Thu, 27 Oct 2011 06:16:31 GMT" ],
          "Content-Type": [ "text/html;charset=utf-8" ],
          "Content-Length": [ "9" ],
          "Server": [ "WEBrick/1.3.1 (Ruby/1.8.7/2011-06-30)" ],
          "Connection": [ "Keep-Alive" ]
        }
      },
      "request": {
        "uri": "http://localhost:7777/foo",
        "body": {
          "encoding": "UTF-8",
          "string": ""
        },
        "method": "get",
        "headers": {
          "Accept-Encoding": [ "identity" ]
        }
      },
      "recorded_at": "Tue, 01 Nov 2011 04:58:44 GMT"
    },
    {
      "response": {
        "body": {
          "encoding": "UTF-8",
          "string": "Hello bar"
        },
        "http_version": null,
        "status": { "code": 200, "message": "OK" },
        "headers": {
          "Date": [ "Thu, 27 Oct 2011 06:16:31 GMT" ],
          "Content-Type": [ "text/html;charset=utf-8" ],
          "Content-Length": [ "9" ],
          "Server": [ "WEBrick/1.3.1 (Ruby/1.8.7/2011-06-30)" ],
          "Connection": [ "Keep-Alive" ]
        }
      },
      "request": {
        "uri": "http://localhost:7777/bar",
        "body": {
          "encoding": "UTF-8",
          "string": ""
        },
        "method": "get",
        "headers": {
          "Accept-Encoding": [ "identity" ]
        }
      },
      "recorded_at": "Tue, 01 Nov 2011 04:58:44 GMT"
    }
  ],
  "recorded_with": "VCR 2.0.0"
}

WhenI run ruby cassette_json.rbThenit should pass with:

Hello foo
Hello bar

Request/Response data can be saved as compressed YAMLGivena file named “cassette_compressed.rb” with:

include_http_adapter_for("net/http")

$server = start_sinatra_app do
  get('/:path') { ARGV[0] + ' ' + params[:path] }
end

require 'vcr'

VCR.configure do |c|
  c.hook_into :webmock
  c.cassette_library_dir = 'cassettes'
  c.before_record do |i|
    i.request.uri.sub!(/:\d+/, ':7777')
  end
  c.default_cassette_options = {
    :match_requests_on => [:method, :host, :path]
  }
end

VCR.use_cassette('example', :serialize_with => :compressed) do
  puts response_body_for(:get, "http://localhost:#{$server.port}/foo", nil, 'Accept-Encoding' => 'identity')
  puts response_body_for(:get, "http://localhost:#{$server.port}/bar", nil, 'Accept-Encoding' => 'identity')
end

WhenI run ruby cassette_compressed.rb 'Hello'Thenthe file “cassettes/example.zz” should contain compressed YAML like:

---
http_interactions:
- request:
    method: get
    uri: http://localhost:7777/foo
    body:
      encoding: UTF-8
      string: ""
    headers:
      Accept-Encoding:
      - identity
  response:
    status:
      code: 200
      message: OK
    headers:
      Content-Type:
      - text/html;charset=utf-8
      Content-Length:
      - "9"
    body:
      encoding: UTF-8
      string: Hello foo
  recorded_at: Tue, 01 Nov 2011 04:58:44 GMT
- request:
    method: get
    uri: http://localhost:7777/bar
    body:
      encoding: UTF-8
      string: ""
    headers:
      Accept-Encoding:
      - identity
  response:
    status:
      code: 200
      message: OK
    headers:
      Content-Type:
      - text/html;charset=utf-8
      Content-Length:
      - "9"
    body:
      encoding: UTF-8
      string: Hello bar
  recorded_at: Tue, 01 Nov 2011 04:58:44 GMT
recorded_with: VCR 2.0.0

WhenI run ruby cassette_compressed.rbThenit should pass with:

Hello foo
Hello bar

Request/Response data can be saved using a custom serializerGivena file named “cassette_ruby.rb” with:

include_http_adapter_for("net/http")

$server = start_sinatra_app do
  get('/:path') { ARGV[0] + ' ' + params[:path] }
end

require 'vcr'

# purely for demonstration purposes; obviously, don't actually
# use ruby #inspect / #eval for your serialization...
ruby_serializer = Object.new
class << ruby_serializer
  def file_extension; "ruby"; end
  def serialize(hash); hash.inspect; end
  def deserialize(string); eval(string); end
end

VCR.configure do |c|
  c.hook_into :webmock
  c.cassette_library_dir = 'cassettes'
  c.cassette_serializers[:ruby] = ruby_serializer
  c.before_record do |i|
    i.request.uri.sub!(/:\d+/, ':7777')
  end
  c.default_cassette_options = {
    :match_requests_on => [:method, :host, :path]
  }
end

VCR.use_cassette('example', :serialize_with => :ruby) do
  puts response_body_for(:get, "http://localhost:#{$server.port}/foo", nil, 'Accept-Encoding' => 'identity')
  puts response_body_for(:get, "http://localhost:#{$server.port}/bar", nil, 'Accept-Encoding' => 'identity')
end

WhenI run ruby cassette_ruby.rb 'Hello'Thenthe file “cassettes/example.ruby” should contain ruby like:

{"http_interactions"=>
  [{"request"=>
     {"method"=>"get",
      "uri"=>"http://localhost:7777/foo",
      "body"=>{"encoding"=>"UTF-8", "string"=>""},
      "headers"=>{"Accept"=>["*/*"], "Accept-Encoding"=>["identity"], "User-Agent"=>["Ruby"]}},
    "response"=>
     {"status"=>{"code"=>200, "message"=>"OK "},
      "headers"=>
       {"Content-Type"=>["text/html;charset=utf-8"],
        "Content-Length"=>["9"],
        "Connection"=>["Keep-Alive"]},
      "body"=>{"encoding"=>"UTF-8", "string"=>"Hello foo"},
      "http_version"=>nil},
    "recorded_at"=>"Tue, 01 Nov 2011 04:58:44 GMT"},
   {"request"=>
     {"method"=>"get",
      "uri"=>"http://localhost:7777/bar",
      "body"=>{"encoding"=>"UTF-8", "string"=>""},
      "headers"=>{"Accept"=>["*/*"], "Accept-Encoding"=>["identity"], "User-Agent"=>["Ruby"]}},
    "response"=>
     {"status"=>{"code"=>200, "message"=>"OK "},
      "headers"=>
       {"Content-Type"=>["text/html;charset=utf-8"],
        "Content-Length"=>["9"],
        "Connection"=>["Keep-Alive"]},
      "body"=>{"encoding"=>"UTF-8", "string"=>"Hello bar"},
      "http_version"=>nil},
    "recorded_at"=>"Tue, 01 Nov 2011 04:58:44 GMT"}],
 "recorded_with"=>"VCR 2.0.0"}

WhenI run ruby cassette_ruby.rbThenit should pass with:

Hello foo
Hello bar

Naming
When inserting or using a cassette, the first argument is the cassette name.
You can use any string for the name. If you use the default :file_system
storage backend, VCR will sanitize the string before using it as a file name,
so that it is a file-system friendly file name.

Scenarios
Name sanitizing
Given
a file named "name_sanitizing.rb" with:
$server = start_sinatra_app do
  get('/') { "Hello" }
end

require 'vcr'

VCR.configure do |c|
  c.cassette_library_dir = 'cassettes'
  c.hook_into :webmock
end

VCR.use_cassette('Fee, Fi Fo Fum') do
  Net::HTTP.get_response('localhost', '/', $server.port)
end
And
the directory "cassettes" does not exist
When
I run ruby name_sanitizing.rb
Then
the file "cassettes/Fee_Fi_Fo_Fum.yml" should contain "Hello"

dsfgsadf

Error for HTTP request made when no cassette is in use

VCR is designed to help you remove all HTTP dependencies from your
test suite. To assist with this, VCR will cause an exception to be
raised when an HTTP request is made while there is no cassette in
use. The error is helpful to pinpoint where HTTP requests are
made so you can use a VCR cassette at that point in your code.

If you want insight about how VCR attempted to handle the request,
you can use the debug_logger
configuration option to log more details.

If you want to allow an HTTP request to proceed as normal, you can
set the allow_http_connections_when_no_cassette
configuration option or you can temporarily turn VCR off:

  • VCR.turn_off! => turn VCR off so HTTP requests are allowed. Cassette insertions will trigger an error.
  • VCR.turn_off!(:ignore_cassettes => true) => turn VCR off and ignore cassette insertions (so that no error is raised).
  • VCR.turn_on! => turn VCR back on
  • VCR.turned_off { ... } => turn VCR off for the duration of the provided block.

Scenarios

  1. Error for request when no cassette is in use
  2. Temporarily turn VCR off to allow HTTP requests to proceed as normal
  3. Turning VCR off prevents cassettes from being inserted
  4. Turning VCR off with `:ignore_cassettes => true` ignores cassettes
  • @exclude-jruby

Error for request when no cassette is in useGivena file named “no_cassette_error.rb” with:

include_http_adapter_for("http_lib")

require 'vcr'

VCR.configure do |c|
  configuration
  c.cassette_library_dir = 'cassettes'
end

response_body_for(:get, 'http://example.com/')

WhenI run ruby no_cassette_error.rbThenit should fail with “An HTTP request has been made that VCR does not know how to handle”

configurationhttp_lib
c.hook_into :webmocknet/http
c.hook_into :webmockhttpclient
c.hook_into :webmockcurb
c.hook_into :webmockpatron
c.hook_into :webmockem-http-request
c.hook_into :webmocktyphoeus
c.hook_into :typhoeustyphoeus
c.hook_into :exconexcon
c.hook_into :faradayfaraday (w/ net_http)

Temporarily turn VCR off to allow HTTP requests to proceed as normalGivena file named “turn_off_vcr.rb” with:

$server = start_sinatra_app do
  get('/') { 'Hello' }
end

require 'vcr'

VCR.configure do |c|
  c.hook_into :webmock
end
WebMock.allow_net_connect!

def make_request(context)
  puts context
  puts Net::HTTP.get_response('localhost', '/', $server.port).body
rescue => e
  puts "Error: #{e.class}"
end

VCR.turned_off do
  make_request "In VCR.turned_off block"
end

make_request "Outside of VCR.turned_off block"

VCR.turn_off!
make_request "After calling VCR.turn_off!"

VCR.turned_on do
  make_request "In VCR.turned_on block"
end

VCR.turn_on!
make_request "After calling VCR.turn_on!"

WhenI run ruby turn_off_vcr.rbThenthe output should contain:

In VCR.turned_off block
Hello

Andthe output should contain:

Outside of VCR.turned_off block
Error: VCR::Errors::UnhandledHTTPRequestError

Andthe output should contain:

After calling VCR.turn_off!
Hello

Andthe output should contain:

In VCR.turned_on block
Error: VCR::Errors::UnhandledHTTPRequestError

Andthe output should contain:

After calling VCR.turn_on!
Error: VCR::Errors::UnhandledHTTPRequestError

Turning VCR off prevents cassettes from being insertedGivena file named “turn_off_vcr_and_insert_cassette.rb” with:

require 'vcr'

VCR.configure do |c|
  c.hook_into :webmock
end
WebMock.allow_net_connect!

VCR.turn_off!
VCR.insert_cassette('example')

WhenI run ruby turn_off_vcr_and_insert_cassette.rbThenit should fail with “VCR is turned off. You must turn it on before you can insert a cassette.”Turning VCR off with `:ignore_cassettes => true` ignores cassettesGivena file named “turn_off_vcr_and_insert_cassette.rb” with:

$server = start_sinatra_app do
  get('/') { 'Hello' }
end

require 'vcr'

VCR.configure do |c|
  c.cassette_library_dir = 'cassettes'
  c.hook_into :webmock
end
WebMock.allow_net_connect!

VCR.turn_off!(:ignore_cassettes => true)

VCR.use_cassette('example') do
  response = Net::HTTP.get_response('localhost', '/', $server.port).body
  puts "Response: #{response}"
end

WhenI run ruby turn_off_vcr_and_insert_cassette.rbThenit should pass with “Response: Hello”Andthe file “cassettes/example.yml” should not exist

Dynamic ERB cassettes

By default, cassettes are static: the exact response that was received
when the cassette was recorded will be replayed for all future requests.
Usually, this is fine, but in some cases you need something more dynamic.
You can use ERB for this.

To enable ERB evaluation of a cassette, pass the :erb => true option
to a cassette. If you want to pass variables to the cassette, you can
pass the names and values of the variables in a hash (:erb => { ... }).ScenariosEnable dynamic ERB cassette evalutation using :erb => trueGivena previously recorded cassette file “cassettes/dynamic.yml” with:

---
http_interactions:
- request:
    method: get
    uri: http://example.com/foo?a=<%= 'b' * 3 %>
    body:
      encoding: UTF-8
      string: ''
    headers: {}
  response:
    status:
      code: 200
      message: OK
    headers:
      Content-Type:
      - text/html;charset=utf-8
      Content-Length:
      - '9'
    body:
      encoding: UTF-8
      string: Hello <%= 'bar'.next %>
    http_version: '1.1'
  recorded_at: Tue, 01 Nov 2011 04:58:44 GMT
recorded_with: VCR 2.0.0

Anda file named “dynamic_erb_example.rb” with:

require 'vcr'

VCR.configure do |c|
  c.hook_into :webmock
  c.cassette_library_dir = 'cassettes'
end

VCR.use_cassette('dynamic', :erb => true) do
  response = Net::HTTP.get_response('example.com', '/foo?a=bbb')
  puts "Response: #{response.body}"
end

WhenI run ruby dynamic_erb_example.rbThenit should pass with “Response: Hello bas”Pass arguments to the ERB using :erb => { … }Givena previously recorded cassette file “cassettes/dynamic.yml” with:

---
http_interactions:
- request:
    method: get
    uri: http://example.com/foo?a=<%= arg1 %>
    body:
      encoding: UTF-8
      string: ''
    headers: {}
  response:
    status:
      code: 200
      message: OK
    headers:
      Content-Type:
      - text/html;charset=utf-8
      Content-Length:
      - '9'
    body:
      encoding: UTF-8
      string: Hello <%= arg2 %>
    http_version: '1.1'
  recorded_at: Tue, 01 Nov 2011 04:58:44 GMT
recorded_with: VCR 2.0.0

Anda file named “dynamic_erb_example.rb” with:

require 'vcr'

VCR.configure do |c|
  c.hook_into :webmock
  c.cassette_library_dir = 'cassettes'
end

VCR.use_cassette('dynamic', :erb => { :arg1 => 7, :arg2 => 'baz' }) do
  response = Net::HTTP.get_response('example.com', '/foo?a=7')
  puts "Response: #{response.body}"
end

WhenI run ruby dynamic_erb_example.rbThenit should pass with “Response: Hello baz”

Automatic Re-recording

Over time, your cassettes may get out-of-date. APIs change and sites you
scrape get updated. VCR provides a facility to automatically re-record your
cassettes. Enable re-recording using the :re_record_interval option.

The value provided should be an interval (expressed in seconds) that
determines how often VCR will re-record the cassette. When a cassette
is used, VCR checks the earliest recorded_at timestamp in the cassette;
if more time than the interval has passed since that timestamp,
VCR will use the :all record mode to cause it be re-recorded.BackgroundGivena previously recorded cassette file “cassettes/example.yml” with:

--- 
http_interactions: 
- request: 
    method: get
    uri: http://localhost/
    body: 
      encoding: UTF-8
      string: ""
    headers: {}
  response: 
    status: 
      code: 200
      message: OK
    headers: 
      Content-Length: 
      - "12"
    body: 
      encoding: UTF-8
      string: Old Response
    http_version: "1.1"
  recorded_at: Tue, 01 Nov 2011 04:58:44 GMT
recorded_with: VCR 2.0.0

Anda file named “re_record.rb” with:

$server = start_sinatra_app do
  get('/') { 'New Response' }
end

require 'vcr'

VCR.configure do |c|
  c.hook_into :webmock
  c.cassette_library_dir = 'cassettes'
end

VCR.use_cassette('example', :re_record_interval => 7.days, :match_requests_on => [:method, :host, :path]) do
  puts Net::HTTP.get_response('localhost', '/', $server.port).body
end

ScenariosCassette is not re-recorded when not enough time has passedGivenit is Tue, 07 Nov 2011WhenI run ruby re_record.rbThenthe output should contain “Old Response”Butthe output should not contain “New Response”Andthe file “cassettes/example.yml” should contain “Old Response”Butthe file “cassettes/example.yml” should not contain “New Response”Cassette is re-recorded when enough time has passedGivenit is Tue, 09 Nov 2011WhenI run ruby re_record.rbThenthe output should contain “New Response”Butthe output should not contain “Old Response”Andthe file “cassettes/example.yml” should contain “New Response”Butthe file “cassettes/example.yml” should not contain “Old Response”

Exclusive cassette

VCR allows cassettes to be nested. This is particularly useful in
a context like cucumber, where you may be using a cassette for an
entire scenario, and also using a cassette within a particular step
definition.

By default, both the inner and outer cassettes are active. On each
request, VCR will look for a matching HTTP interaction in the inner
cassette, and it will use the outer cassette as a fall back if none
can be found.

If you do not want the HTTP interactions of the outer cassette considered,
you can pass the :exclusive option, so that the inner cassette is
used exclusively.BackgroundGivena previously recorded cassette file “cassettes/outer.yml” with:

--- 
http_interactions: 
- request: 
    method: get
    uri: http://localhost/outer
    body: 
      encoding: UTF-8
      string: ""
    headers: {}
  response: 
    status: 
      code: 200
      message: OK
    headers: 
      Content-Length: 
      - "18"
    body: 
      encoding: UTF-8
      string: Old outer response
    http_version: "1.1"
  recorded_at: Tue, 01 Nov 2011 04:58:44 GMT
recorded_with: VCR 2.0.0

Anda previously recorded cassette file “cassettes/inner.yml” with:

--- 
http_interactions: 
- request: 
    method: get
    uri: http://localhost/inner
    body: 
      encoding: UTF-8
      string: ""
    headers: {}
  response: 
    status: 
      code: 200
      message: OK
    headers: 
      Content-Length: 
      - "18"
    body: 
      encoding: UTF-8
      string: Old inner response
    http_version: "1.1"
  recorded_at: Tue, 01 Nov 2011 04:58:44 GMT
recorded_with: VCR 2.0.0

Anda file named “setup.rb” with:

include_http_adapter_for("net/http")

$server = start_sinatra_app do
  get('/:path') { "New #{params[:path]} response" }
end

require 'vcr'

VCR.configure do |c|
  c.hook_into :webmock
  c.cassette_library_dir = 'cassettes'
  c.default_cassette_options = {
    :record => :new_episodes,
    :match_requests_on => [:method, :host, :path]
  }
end

ScenariosCassettes are not exclusive by defaultGivena file named “not_exclusive.rb” with:

require 'setup'

VCR.use_cassette('outer') do
  VCR.use_cassette('inner') do
    puts response_body_for(:get, "http://localhost:#{$server.port}/outer")
    puts response_body_for(:get, "http://localhost:#{$server.port}/inner")
  end
end

WhenI run ruby not_exclusive.rbThenit should pass with:

Old outer response
Old inner response

Use an exclusive cassetteGivena file named “exclusive.rb” with:

require 'setup'

VCR.use_cassette('outer') do
  VCR.use_cassette('inner', :exclusive => true) do
    puts response_body_for(:get, "http://localhost:#{$server.port}/outer")
    puts response_body_for(:get, "http://localhost:#{$server.port}/inner")
  end
end

WhenI run ruby exclusive.rbThenit should pass with:

New outer response
Old inner response

Andthe file “cassettes/inner.yml” should contain “New outer response”

Update content_length header

When the :update_content_length_header option is set to a truthy value,
VCR will ensure that the Content-Length header will have the correct
value. This is useful in several situations:

  • When you manually edit the cassette file and change the resonse body length. You can use this option so you don’t have to manually calculate and update the body length.
  • When you use ERB, the response body length may vary. This will ensure it is always correct.
  • Syck, the default YAML engine for ruby 1.8 (and 1.9, unless you compile it to use Psych), has a bug where it sometimes will remove some whitespace strings when you serialize them. This may cause the Content-Length header to have the wrong value.

This is especially important when you use a client that checks the
Content-Length header. Mechanize, for example, will raise an EOFError
when the header value does not match the actual body length.BackgroundGivena previously recorded cassette file “cassettes/example.yml” with:

--- 
http_interactions: 
- request: 
    method: get
    uri: http://example.com/
    body: 
      encoding: UTF-8
      string: ""
    headers: {}
  response: 
    status: 
      code: 200
      message: OK
    headers: 
      Content-Type: 
      - text/html;charset=utf-8
      Content-Length: 
      - "11"
    body: 
      encoding: UTF-8
      string: Hello <modified>
    http_version: "1.1"
  recorded_at: Tue, 01 Nov 2011 04:58:44 GMT
recorded_with: VCR 2.0.0

Anda file named “common_stuff.rb” with:

require 'vcr'

VCR.configure do |c|
  c.cassette_library_dir = 'cassettes'
  c.hook_into :webmock
end

def make_request_and_print_results
  response = Net::HTTP.get_response('example.com', '/')
  puts "Body length: #{response.body.length}"
  puts "Header value: #{response['Content-Length']}"
end

Scenarios

  1. Default :update_content_length_header setting
  2. :update_content_length_header => false
  3. :update_content_length_header => true

Default :update_content_length_header settingGivena file named “default.rb” with:

require 'common_stuff'

VCR.use_cassette('example') do
  make_request_and_print_results
end

WhenI run ruby default.rbThenthe output should contain:

Body length: 16
Header value: 11

:update_content_length_header => falseGivena file named “false.rb” with:

require 'common_stuff'

VCR.use_cassette('example', :update_content_length_header => false) do
  make_request_and_print_results
end

WhenI run ruby false.rbThenthe output should contain:

Body length: 16
Header value: 11

:update_content_length_header => trueGivena file named “true.rb” with:

require 'common_stuff'

VCR.use_cassette('example', :update_content_length_header => true) do
  make_request_and_print_results
end
Body length: 16
Header value: 16

Decode compressed response

When the :decode_compressed_response option is set to a truthy value, VCR
will decompress “gzip” and “deflate” response bodies before recording. This
ensures that these interactions become readable and editable after being
serialized.

This option should be avoided if the actual decompression of response bodies
is part of the functionality of the library or app being tested.BackgroundGivena file named “decompress.rb” with:

require 'zlib'
require 'stringio'

$server = start_sinatra_app do
  get('/') {
    content = 'The quick brown fox jumps over the lazy dog'
    io = StringIO.new

    writer = Zlib::GzipWriter.new(io)
    writer << content
    writer.close

    headers['Content-Encoding'] = 'gzip'
    io.string
  }
end

require 'vcr'

VCR.configure do |c|
  c.cassette_library_dir = 'cassettes'
  c.hook_into :webmock
  c.default_cassette_options = { :serialize_with => :syck }
end

ScenariosThe option is not set by defaultWhenI append to file “decompress.rb”:

VCR.use_cassette(:decompress) do
  Net::HTTP.start('localhost', $server.port) do |http|
    http.get('/', 'accept-encoding' => 'identity')
  end
end

AndI run ruby decompress.rbThenthe file “cassettes/decompress.yml” should contain a YAML fragment like:

Content-Encoding:
- gzip

The option is enabledWhenI append to file “decompress.rb”:

VCR.use_cassette(:decompress, :decode_compressed_response => true) do
  Net::HTTP.start('localhost', $server.port) do |http|
    http.get('/', 'accept-encoding' => 'identity')
  end
end

AndI run ruby decompress.rbThenthe file “cassettes/decompress.yml” should contain a YAML fragment like:

Content-Length:
- '43'

Andthe file “cassettes/decompress.yml” should contain:

Allow Unused HTTP Interactions

If set to false, this cassette option will cause VCR to raise an error
when a cassette is ejected and there are unused HTTP interactions remaining,
unless there is already an exception unwinding the callstack.

It verifies that all requests included in the cassette were made, and allows
VCR to function a bit like a mock object at the HTTP layer.

The option defaults to true (mostly for backwards compatibility).BackgroundGivena file named “vcr_config.rb” with:

require 'vcr'

VCR.configure do |c|
  c.hook_into :webmock
  c.cassette_library_dir = 'cassettes'
end

Anda previously recorded cassette file “cassettes/example.yml” with:

--- 
http_interactions: 
- request: 
    method: get
    uri: http://example.com/foo
    body: 
      encoding: UTF-8
      string: ""
    headers: {}
  response: 
    status: 
      code: 200
      message: OK
    headers: 
      Content-Length: 
      - "5"
    body: 
      encoding: UTF-8
      string: Hello
    http_version: "1.1"
  recorded_at: Tue, 01 Nov 2011 04:58:44 GMT
recorded_with: VCR 2.0.0

Scenarios

  1. Unused HTTP interactions are allowed by default
  2. Error raised if option is false and there are unused interactions
  3. No error raised if option is false and all interactions are used
  4. Does not silence other errors raised in `use_cassette` block

Unused HTTP interactions are allowed by defaultGivena file named “allowed_by_default.rb” with:

require 'vcr_config'

VCR.use_cassette("example") do
  # no requests
end

WhenI run ruby allowed_by_default.rbThenit should passError raised if option is false and there are unused interactionsGivena file named “disallowed_with_no_requests.rb” with:

require 'vcr_config'

VCR.use_cassette("example", :allow_unused_http_interactions => false) do
  # no requests
end

WhenI run ruby disallowed_with_no_requests.rbThenit should fail with an error like:

There are unused HTTP interactions left in the cassette:
  - [get http://example.com/foo] => [200 "Hello"]

No error raised if option is false and all interactions are usedGivena file named “disallowed_with_all_requests.rb” with:

require 'vcr_config'

VCR.use_cassette("example", :allow_unused_http_interactions => false) do
  Net::HTTP.get_response(URI("http://example.com/foo"))
end

WhenI run ruby disallowed_with_all_requests.rbThenit should passDoes not silence other errors raised in `use_cassette` blockGivena file named “does_not_silence_other_errors.rb” with:

require 'vcr_config'

VCR.use_cassette("example", :allow_unused_http_interactions => false) do
  raise "boom"
end

WhenI run ruby does_not_silence_other_errors.rbThenit should fail with “boom”Andthe output should not contain “There are unused HTTP interactions”

Freezing Time

When dealing with an HTTP API that includes time-based compontents
in the request (e.g. for signed S3 requests), it can be useful
on playback to freeze time to what it originally was when the
cassette was recorded so that the request is always the same
each time your test is run.

While VCR doesn’t directly support time freezing, it does
expose VCR::Cassette#originally_recorded_at, which you can
easily use with a library like
timecop
to freeze time.

Note: VCR::Cassette#originally_recorded_at will return nil
when the cassette is recording for the first time, so you’ll
probably want to use an expression like
cassette.originally_recorded_at || Time.now so that it
will work when recording or when playing back.ScenariosPreviously recorded responses are replayedGivena previously recorded cassette file “cassettes/example.yml” with:

--- 
http_interactions: 
- request: 
    method: get
    uri: http://example.com/events/since/2013-09-23T17:00:30Z
    body: 
      encoding: UTF-8
      string: ""
    headers: {}
  response: 
    status: 
      code: 200
      message: OK
    headers: 
      Content-Length: 
      - "20"
    body: 
      encoding: UTF-8
      string: Some Event
    http_version: "1.1"
  recorded_at: Mon, 23 Sep 2013 17:00:30 GMT
recorded_with: VCR 2.0.0

Givena file named “freeze_time.rb” with:

require 'time'
require 'timecop'
require 'vcr'

VCR.configure do |vcr|
  vcr.cassette_library_dir = 'cassettes'
  vcr.hook_into :webmock
end

VCR.use_cassette('example') do |cassette|
  Timecop.freeze(cassette.originally_recorded_at || Time.now) do
    path = "/events/since/#{Time.now.getutc.iso8601}"
    response = Net::HTTP.get_response('example.com', path)
    puts "Response: #{response.body}"
  end
end

WhenI run ruby freeze_time.rbThenit should pass with “Response: Some Event”