Skip to content

Instantly share code, notes, and snippets.

@hisapy
Last active August 29, 2015 14:11
Show Gist options
  • Save hisapy/5d5b04aa894f21e15788 to your computer and use it in GitHub Desktop.
Save hisapy/5d5b04aa894f21e15788 to your computer and use it in GitHub Desktop.
RSpec: call VCR registered request matcher from inside custom matcher
# This is an excerpt from a spec_helper file with a section to configure VCR
#
# In lines 30 and 32 you can see examples of how to call a VCR registered request matcher from inside another.
# This is useful when running an example that makes a request to multiple external resources or APIs.
# Without this the requests were recorded the 1st time but subsequent requests raised VCR::Errors::UnhandledHTTPRequestError
VCR.configure do |c|
c.ignore_localhost = true
c.cassette_library_dir = 'spec/cassettes'
c.hook_into :webmock
c.allow_http_connections_when_no_cassette = false
c.configure_rspec_metadata!
#Custom Matchers
c.register_request_matcher :w3_css_validator, &VCR.request_matchers.uri_without_params(:output, :profile, :text)
c.register_request_matcher :s3_paperclip do |r1, r2|
#these .match might not be needed
r1_path = URI(r1.uri).path.match("#{CONFIG[:amazon][:s3][:bucket]}/").to_s
r2_path = URI(r2.uri).path.match("#{CONFIG[:amazon][:s3][:bucket]}/").to_s
if URI(r1.uri).host != "s3.amazonaws.com" || !r1_path
false
else
URI(r1.uri).host == URI(r2.uri).host && r1_path == r2_path
end
end
c.register_request_matcher :css_validator_and_s3_requests do | r1, r2|
if URI(r1.uri).host == "jigsaw.w3.org"
VCR.request_matchers[:w3_css_validator].callable.call(r1, r2)
elsif URI(r1.uri).host == "s3.amazonaws.com"
VCR.request_matchers[:s3_paperclip].callable.call(r1, r2)
end
end
end
@hisapy
Copy link
Author

hisapy commented Dec 13, 2014

Is there another approach to run an example where the subject makes 2 (or more) requests to external resources or APIs?

@myronmarston
Copy link

Is there another approach to run an example where the subject makes 2 (or more) requests to external resources or APIs?

In general yes -- I've used VCR many, many times where it made multiple requests to multiple external APIs from a single acceptance test that all recorded it into one VCR cassette. For your specific case, it's hard to provide a suggestion for a better approach without digging into the specifics of the requests that get made in by your test suite and what parts of those requests are non-deterministic. I honestly don't have the time to dig into that kind of detail (unless you want to pay for my time), but here are a few general suggestions:

  • The uri_without_params matcher is completely safe to use on requests which do not have the named parameters (URIs that lack those params will be compared unmodified). So unless your test suite has some requests that are made with output, profile or text query params where you don't want to ignore them, there's really no reason to apply that matcher only to "jigsaw.w3.org" requests.
  • While it's generally a good idea to try to precisely define what attributes of a request VCR should match on, in many/most cases you can get by with very loose matching (which can greatly simplify your code), provided you bear in mind a few things:
    • When playing back, VCR will use the first matching not-yet-played-back HTTP interaction from the cassette.
    • Thus, if your test makes the requests in a consistent, deterministic order, they'll get recorded to the cassette in the same order that they'll get played back in, even with very lax matching logic. Don't do this, but consider if you used match_requests_on: [lambda { |r1, r2| true }]: such a request matcher would consider any request to match any other request, even if they had nothing common. As bad as an idea as that is, it could actually work fine for tests that make the requests in a consistent, deterministic order.
    • The downside to lax request matching is that if the requests in your test are not in a consistent, or something changes that makes your test make a new request, VCR could playback the wrong response for that request, so you probably want to at least use VCR matchers for a minimum baseline like HTTP method, host and perhaps path.
    • Using the :new_episodes record mode in these sorts of situations is also potentially problematic, because when a new, unmatched request is made, VCR will append that interaction to the end of the cassette, even if it's a new request from the start or middle of the test -- and then when playback happens on the next test run it could replay the wrong response for a request. It's better to use the :once record mode as it only allows a cassette to be recorded once, preventing out-of-order-requests from being appended later.

Good luck!

@hisapy
Copy link
Author

hisapy commented Dec 15, 2014

Thanks for taking your time to comment on this gist Myron. I'm using record: :once and have been using :w3_css_validator and :s3_paperclip separately before creating the :css_validator_and_s3_requests to handle requests of a model that queries those APIs at the same time. I'll read your suggestions carefully and see if I can simplify these requests matching logic.

Thanks a lot again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment