Skip to main content
Skip table of contents

Testing with VCR

Last Updated:

VCR can be a great way to record HTTP interactions in a controlled environment and replay those interactions during tests. We don’t want to make actual HTTP requests during a test scenario, so VCR helps us get around that by recording “cassettes” (YAML representations of the request to an external service and its response).

Recording VCR cassettes

Record (or re-record) a cassette using the following general steps:

  1. Establish connection to upstream service - To record a VCR cassette, you must make a successful network request. Across Vets API, there are several approaches to connecting to the upstream service depending on the level of authorization required. Please see below for Testing with VCR: Approaches to recording cassettes.

  2. Prepare destination for cassette - Essentially, you must not have an existing file matching the declared cassette, or more specifically an http_interaction in the cassette matching whatever constraints you’ve set (method, path, headers, etc), or VCR will use that interaction instead of making a new request.

    1. if you want to store your interaction in spec/vcr_cassettes/my_module/health_check.yml, the syntax would look something like the following:

      1. CODE
        VCR.use_cassette('my_module/health_check') do
          ...
        end
    2. In most instances, you’ll likely be recording all interactions for a single request in a single cassette. VCR will create the file for you, so simply confirm the file doesn’t exist. If you’re re-recording a cassette, just delete the existing one, as a new one will replace it.

  3. Run the spec/example - I would generally recommend recording one example/cassette at a time, and to be sure to confirm that the cassette recorded what was expected. Theoretically your specs should test the important parts of the response, but it can/should be used to confirm the request is being constructed properly. If something doesn’t look correct or if you modify your code, just delete the cassette and run it again.

Approaches to recording cassettes

A variety of methods are used across Vets API and its modules to record HTTP interactions as VCR Cassettes. Consult your team to determine which method is necessary, depending on the nature of the service you are hitting. Several modules' READMEs contain information on how to record their cassettes, and some files/examples have notes about it as well. If you are attempting to re-record a cassette and can’t find information about how to do it, try the following methods. I’ve added a few teams/modules that use the respective methods (and feel free to add your team if you follow that approach as well!)

NOTE: If you/your team has a particularly complicated or undocumented approach to recording cassettes, I urge you to add notes in the respective file, add to a relevant README, please submit feedback using the Help and feedback footer at the bottom of this page. It will undoubtedly make someone’s life easier down the road.

Some spec files may contain comments towards the top with instructions, while others may have relevant information in a single place. Here are a few places you can look:

Connecting using local settings/test credentials

In some cases, authenticating with the upstream service is as easy as having a token/the correct settings enabled during the test.

Important things to keep in mind:

  • You will likely need to override some settings, perhaps even in the spec file itself. Remember to undo these specific changes as to not commit anything unintended (tokens/API keys, etc). Carefully compare your diffs for committing/merging

Hints:

  1. Try looking at related settings in settings.yml or settings/test.yml. This may have notes or provide hints on what settings must be temporarily updated. You may need to retrieve these values from helm charts (i.e. the staging helm chart) or from the parameter store.

  2. Instead of overwriting values in settings.yml or settings/test.yml with the added risk of committing something you don’t wish to, you can just add the key to a settings/test.local.yml file, which will supersede the other files and is git-ignored by default.

Example modules/files:

CODE
/lib
  /central_mail
  /search_click_tracking
  /search_typeahead

Connecting via Forward Proxy

In some cases where the client isn’t required to be on the VA network, a request can be made through a forward proxy. This document provides extensive information on how to SSH to a staging API via a forward proxy, but here are a few notes:

  • If you have correct AWS permissions, you should be able to skip the first 3 steps by simply grabbing the Private IP DNS name of a Staging fwdproxy (If you don’t have access, you may need to find someone who does, as the other steps may not work anyhow)

  • You’ll need to point the host to your localhost port that you’ve identified in your SSH command. For example, if you want to connect to BGS (port 4777), you’ll need to set Settings.bgs.url to https://localhost:4447. Luckily for us, BGS provides a helpful example here.

  • Some services may require other local settings be overwritten, such as ssl_verify_mode in the previous example

Example modules/files:

CODE
/modules
  /claims_api # Benefits Claims API (BGS/EVSS) - README available

Connecting in Review instances

Some requests are bit more complicated, and either have more strict authentication in place, or perhaps hit multiple upstream services, so it’s easier to just connect through an application running on the VA network. Review Instances are your best bet. This document provides a great walkthrough on how to record cassettes in a RI.

NOTE: I’ve found that if you just SSH to your instance in your terminal (ssh ip-XXX-blah.internal), you can just cat the contents of a cassette and copy/paste it to your branch without using the more complicated function described in the final step of the documentation.

Example modules/files:

CODE
/lib
  /form1010_ezr
  /hca

Other Techniques

  • This document describes a team's approach for recording http interactions with VA Profile, in which they create a separate, lightweight app that records a given cassette so that they can copy the result to vets-api and write specs against it

VCR Debugging

Sometimes VCR seems to work out of the box, but sometimes it takes trial and error to get a test to recognize cassettes. Below are some errors and possible resolutions.

Common VCR errors and resolutions

Problem: I’m running my specs, but I’m receiving 5XX errors / can’t seem to connect to the service

How to record a cassette inside the VA network? The URL is only accessible from inside the VA network.

Resolution: It’s likely that you’re not authorized and/or must send the request from the VA network - see Testing with VCR: Approaches to recording cassettes.

Problem: A cassette is being used in a test, but the test doesn’t seem to recognize it. It’s failing as if the cassette is not there.

Possible resolution: Ensure the VCR cassette is spelled correctly and with the correct path. Double check the VCR cassette name. Make sure there is no .yml at the end of a cassette name in the test.

Possible resolution: It could be that there are other VCR cassettes interfering with the one you want to use and can’t find yours. Try using match_requests_on and specifying a unique attribute in your cassette. The :match_requests_on option defaults to [:method, :uri, :body] (those options are set in spec/rails_helper.rb) unless it’s set otherwise in the test. Here’s the request_matching VCR documentation.

This is where the defaults are set in rails_helper.rb in vets-api:

RUBY
module VCR
  def self.all_matches
    %i[method uri body]
  end
end

Example of match_requests_on being used:

RUBY
it '204s when given a null category' do
  VCR.use_cassette('okta/verification-scopes', match_requests_on: %i[method path]) do
    get '/services/apps/v0/directory/scopes'
    expect(response).to have_http_status(:no_content)
  end
end

Possible resolution: Most of the time, many tests can use the same cassette with no problem. But if you are using the same cassette more than once and have an error, try using allow_playback_repeats: true. This might come into play if the test makes multiple API calls to the same endpoint during the course of a single test. Here’s the playback_repeats VCR documentation.

Example:

RUBY
it 'prefills 1990' do
  VCR.use_cassette('va_profile/military_personnel/service_history_200_many_episodes',
                    allow_playback_repeats: true, match_requests_on: %i[uri method body]) do
    expect_prefilled('22-1990')
  end
end

Problem: Generic VCR errors that don’t show where the actual problem is.

Possible resolution: binding.prys for the win! Place binding.pry statements within any methods or controllers that might reference the endpoint. Sometimes a bug in the code can result in generic VCR errors that make it look like it’s a VCR problem, when it’s not.

General warnings

⚠️ Be sure the cassettes that will be pushed to vets-api don’t have any sensitive information. See filter_sensitive_data VCR documentation. This option is being used in vets-api in config/initializers/integration_recorder.rb. Ask yourself if any more sensitive data needs to be added to this list? If in question, reach out to #vfs-platform-support.

Have any more VCR gotchas? Send them to the Platform using the “Suggest content changes to this page” link below.


JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.