VCR and Webmock: Two Ways to Fake HTTP
VCR is a Ruby library that records HTTP interactions and plays them back to your test suite, verifying input and returning predictable output. Both VCR and Webmock accomplish essentially the same goal, but they differ in how much you need to reason about the external API yourself.
With Webmock, you hand-write mocks for a handful of endpoints and return a known set of data. You have to explore and understand the API's behavior yourself, then judge whether your constructed data is valid. This is easy to change but requires that you never alter the Webmock in a way that's incompatible with the actual API.
VCR records an interaction with a live copy of the API and replays it when the same request is made. The API itself supplies the data, and VCR verifies requests at a configurable level of specificity. You need to make sure your collaborating API returns responses that give you the test cases you want. This is harder to change but is guaranteed to accurately represent how the API behaved at the time the cassette was recorded.
Why Hand-Rolled Stubs Fall Short
Mocks have a different primary use case: verifying the attributes of an outgoing message that crosses a system boundary. They shouldn't be used solely to provide data that is difficult to arrange, unless that data has been validated against its source in a separate test. As Martin Fowler has noted, mocks aren't stubs. A stub is more appropriate for returning dummy data, but it normally only responds to a limited set of calls with a limited set of data—there's no guarantee you've faithfully reproduced the behavior of a troublesome API.
Web service stubs also require internal knowledge of your system. You must understand at a low level how the HTTP layer is implemented in a given area, then reach inside and reimplement part of it. A common objection to VCR is that it generates many similar HTTP recordings where a single Webmock stub request would do—but that's usually a design smell. It's almost always better to split collaborators so that external data is injected into a class you control and can easily unit test. With that design, you can write faster unit tests around the transformation logic, and verify the client with mocks as external verifiers.
Exploratory Tests Beat Console Noodling
Poking around in the console to understand how an API works is slow and error-prone. If you find yourself running snippets in the console or adding lots of puts statements to discover what's happening, consider an exploratory test instead. VCR speeds this up because it captures API calls with perfect recall.
Everyone does test-driven development all the time—the question is whether you automate the tedious parts or do them by hand. If you experiment with GraphQL until you figure out what the API returns, then copy that response into a Webmock, you're doing exactly what VCR does, just with extra manual steps.
When VCR Makes Sense (and When It Doesn't)
VCR is not a universal solution. It depends on having a ready copy of the API you're integrating with, which can mean running tests against production endpoints or standing up your own instance. For simple single-call GET operations, the overhead might not be worth it. And if the API changes, the auto-mocks VCR generates won't change—a limitation it shares with Webmock.
Consider VCR when:
- The API call sequence or timing is unpredictable.
- You don't have a good understanding of the API's behavior.
- Multiple API calls are involved in a single logical domain action.
- The API is slow, unreliable, or obnoxious to use.
VCR is probably overkill for:
- Single API calls with well-formed, understood schemas.
- APIs you control and are developing in parallel with the client.
- APIs you understand well with well-defined behavior.
- Calls that mutate state in ways that are difficult to roll back.
The best approach often mixes both tools: use VCR for large integration-style tests, and fall back to Webmock or decompose into unit tests for specific behaviors tied to particular API states that are hard to reproduce but functionally possible.
Getting Started with VCR
Add VCR to your Gemfile and configure it in your test helper. Webmock or a similar library is required. When you want to use it, wrap your callsite in a VCR.use_cassette block. You can configure what counts as a "matching" request via the use_cassette method. Be cautious about letting VCR auto-title and record cassettes based on test names—it will re-record them whenever a test description changes.
Work against the live API as much as possible before recording interactions. To keep VCR from recording while you're still experimenting, configure it to not record anything. VCR creates YAML files containing recorded HTTP interactions—delete these liberally. When in doubt, delete the cassette and re-record it.
Record Policy: Avoid :new_episodes
New episodes silently records requests it can't match against existing ones. Though it's the default, this option is optimized for integration tests that touch many different APIs. Unless that's your use case, consider :once, which only records on first playback, or :none, which disallows new HTTP requests. An exception arises when you split requests to a single API across cassettes to capture interactions with non-deterministic behavior; recording outside a single test run can help there. But don't leave :new_episodes on.
Log Everything with before_filter
This feature logs reasonably complete information about every outgoing request your app generates—handy even if you never use VCR for capturing. The req object is a Net:HTTP request, so more information is available, and you can attach pry or other debuggers.
Redact Secrets with filter_sensitive_data
This method replaces data in cassettes with static placeholders. It's useful for handling dynamic data at runtime and for keeping credentials you need for API access out of checked-in files. Any string that can be evaluated at runtime can be replaced with an easily identifiable placeholder.
Two Advanced Techniques
Split Out Troublesome Collaborators
Some APIs are hard to characterize. Certain authentication APIs get called a random number of times at random intervals depending on when their internal cache expires. To keep these random-but-critical calls out of cassettes for other services, you can use before filters to isolate them into their own cassette. This keeps things tidy but can hide errors, since every HTTP interaction from your tests no longer lives in a single YAML file.
Inspect Your YAML Files
Even if you don't use VCR's auto-mocks, recorded HTTP interactions are useful for debugging. You can compare them against other interactions, spot odd arguments or responses, or modify them directly to simulate difficult-to-reproduce scenarios. You can even embed ERB in the YAML files to provide dynamic content.
The Verdict
VCR is a powerful tool for systematizing interactions with HTTP APIs. Whether it's right for your project depends on your constraints—as always. If you're struggling with hard-to-maintain mocks, misbehaving APIs, or complex multi-step interactions, and want tests that are more reliable, faster, and easier to debug, VCR can get you there.



