Warm tip: This article is reproduced from serverfault.com, please click

Ruby VCR gem keeps recording the same requests

发布于 2012-07-11 21:03:01

In my cucumber support directory I have the following in vcr.rb:

require 'vcr'

VCR.configure do |c|
  c.cassette_library_dir = 'fixtures/vcr_cassettes'
  c.hook_into :webmock
  c.ignore_localhost = true
  c.default_cassette_options = { record: :new_episodes }
end

I am geocoding city names which makes calls to the Google Maps API. I'm trying to record and stub these requests, but it keeps recording the same requests to the same yml file:

- request:
    method: get
    uri: http://maps.googleapis.com/maps/api/geocode/json?address=Miami,%20FL&language=en&sensor=false
    body:
      encoding: US-ASCII
      string: ''
    headers:
      Accept-Encoding:
      - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
      Accept:
      - ! '*/*'
      User-Agent:
      - Ruby
  # response...

- request:
    method: get
    uri: http://maps.googleapis.com/maps/api/geocode/json?address=Miami,%20FL&language=en&sensor=false
    body:
      encoding: US-ASCII
      string: ''
    headers:
      Accept-Encoding:
      - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
      Accept:
      - ! '*/*'
      User-Agent:
      - Ruby

It's the same URL and very same request, shouldn't VCR stub the request? How can I prevent my specs from hitting the API every time I try to search for the same city?

Questioner
Erik J
Viewed
0
Myron Marston 2012-07-12 09:40:13

It's hard to say for sure what's going on with just what you've posted, but I can explain a bit more about how VCR works and make some guesses about possible reasons for this behavior.

VCR uses request matchers to try to find a previously recorded HTTP interaction to play back. During a single cassette session, when an HTTP interaction is played back, it is considered to be "used" and it will not be played back again (unless you use the allow_playback_repeats option).

So...here are a couple possibilities that come to mind:

  • Maybe VCR is unable to match your HTTP requests. What request matchers are you using? There are some easy ways to troubleshoot this (see below).
  • If you're not using :allow_playback_repeats (which is the default, and how I recommend you use VCR), then the behavior you're seeing could happen if multiple duplicate requests are being made in your test--e.g., maybe the cassette has only one matching request but you're test makes 2 of them--that would play back one and record one (since you're using :new_episodes).

To troubleshoot this, I recommend you use the debug_logger option to get VCR to print what it's doing and how it's trying to match each request. That should give you insight into what's going on. You can also override any of the built in request matchers and provide your own logic and/or set a breakpoint in the matcher:

VCR.configure do |c|
  c.register_request_matcher :uri do |request_1, request_2|
    debugger # so you can inspect the requests here
    request_1.uri == request_2.uri
  end
end

You may also have run into a VCR bug, although comparing the URIs (using String#==) is such a basic operation that I have a hard time imagining a bug there. Feel free to open a github issue (hopefully with the debug logger output and/or a code sample that triggers this) if you can't figure it out.

On a side note, I recommend you use the :once record mode (the default) rather than :new_episodes. :once will never record additional HTTP interactions to an existing cassette--it only allows the cassette to be recorded once. If a request fails to match, it'll raise an error alerting you to the fact it couldn't match. :new_episodes, on the other hand, will record any request that it can't find a match for, which is the behavior you're seeing.