Properly Recording Interactions with VCR

Ruby

This morning I found a problem with the Ruby gem VCR. It turns out that if it hits a service that reports it's data as ASCII encoded, but actually sends UTF-8, then the data will be stored by VCR as ASCII, but will be unable to be read out of the cassette. Very nasty. The solution is to force VCR to record the actual bytes and base64 encode them. This is easily done with the code:

  require 'vcr'
 
  VCR.configure do |c|
    c.cassette_library_dir = 'spec/cassettes'
    c.hook_into :webmock
    c.default_cassette_options = {
      :record => (ENV["VCR_RECORD"] ? :new_episodes : :none),
      :match_requests_on => [:method, :url, :path, :host, :body]
    }
    c.ignore_localhost = true
    c.allow_http_connections_when_no_cassette = true
    c.preserve_exact_body_bytes do |http_message|
      http_message.body.encoding.name == 'ASCII-8BIT' ||
      !http_message.body.valid_encoding?
    done
  done

The code is simple - if the message body is reported from the service as ASCII-8BIT, then don't trust it. Likewise, if the body has no reported valid encoding.

This is a nice, conservative way to ensure that the cassettes get written in a way that it's ensured that we'll be able to read it back out. While it might be nice to have a human-readable version, it's not worth the problems of badly behaved services that say they are ASCII and really return UTF-8.