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
- status
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
- Request/Response data is saved to disk as YAML by default
- Request/Response data can be saved as JSON
- Request/Response data can be saved as compressed YAML
- 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
configuration | http_lib |
---|---|
c.hook_into :webmock | net/http |
c.hook_into :webmock | httpclient |
c.hook_into :webmock | patron |
c.hook_into :webmock | curb |
c.hook_into :webmock | em-http-request |
c.hook_into :webmock | typhoeus |
c.hook_into :typhoeus | typhoeus |
c.hook_into :excon | excon |
c.hook_into :faraday | faraday (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.rb
Thenit 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.rb
Thenit 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.rb
Thenit 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 onVCR.turned_off { ... }
=> turn VCR off for the duration of the provided block.
Scenarios
- Error for request when no cassette is in use
- Temporarily turn VCR off to allow HTTP requests to proceed as normal
- Turning VCR off prevents cassettes from being inserted
- 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.rb
Thenit should fail with “An HTTP request has been made that VCR does not know how to handle”
configuration | http_lib |
---|---|
c.hook_into :webmock | net/http |
c.hook_into :webmock | httpclient |
c.hook_into :webmock | curb |
c.hook_into :webmock | patron |
c.hook_into :webmock | em-http-request |
c.hook_into :webmock | typhoeus |
c.hook_into :typhoeus | typhoeus |
c.hook_into :excon | excon |
c.hook_into :faraday | faraday (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.rb
Thenthe 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.rb
Thenit 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.rb
Thenit 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.rb
Thenit 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.rb
Thenit 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.rb
Thenthe 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.rb
Thenthe 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.rb
Thenit 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.rb
Thenit 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 theContent-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
- Default :update_content_length_header setting
- :update_content_length_header => false
- :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.rb
Thenthe 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.rb
Thenthe 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.rb
Thenthe 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.rb
Thenthe 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
- Unused HTTP interactions are allowed by default
- Error raised if option is false and there are unused interactions
- No error raised if option is false and all interactions are used
- 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.rb
Thenit 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.rb
Thenit 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.rb
Thenit 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.rb
Thenit 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 likecassette.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.rb
Thenit should pass with “Response: Some Event”