What is ExVCR?
Record and replay HTTP interactions library for elixir. It’s inspired by Ruby’s VCR, and trying to provide similar functionalities.
ExVCR allows you to automatically record any HTTP request/response to a JSON text file. Subsequent requests matching the same URL are served up by the cached response from disk. The feedback loop is minimised when following a test-driven development approach due to the instant HTTP responses. The recorded fixtures support offline development, help other developers, and allow test execution without external HTTP requests in continuous integration environments.
Strava enforces a rate limit to its REST API. The default rate limit allows 600 requests every 15 minutes, with up to 30,000 requests per day. I also use the mix test.watch task to execute the test suite after any file is saved. The rate limit and frequent test runs meant that ExVCR was an ideal fit.
The Strava library uses HTTPoison as the HTTP client. This uses hackney to execute the HTTP requests. Therefore I had to use the
ExVCR.Adapter.Hackney adapter in my tests.
Using ExVCR in a unit test requires the following code changes.
exvcr to the project’s dependencies in
config/mix.exs as a test-only dependency.
2. Disable ExUnit’s async support.
3. Use the ExVCR mock macro with the
HTTPoison to start for all tests.
5. Wrap the lines of code making the HTTP request inside a
The string provided to
use_cassette is used to build the path to the recorded JSON file, relative to
fixture/vcr_cassettes. So you can use the path separator character to group related fixtures together in the same directory.
The full code for the sample test is given below.
The first time you run the test there will be no ExVCR fixture data. So HTTP requests are made and the response is cached to disk at
fixture/vcr_cassettes. Subsequent test runs will use the cached fixture data if it matches the requested URL.
For the example test above, the cached fixture JSON data is given below.
To execute the unit tests against the real web services you simply delete the stored fixture files.
Query string parameters
By default, query params are not used for matching with URLS recorded in ExVCR fixtures. You must specify
match_requests_on: [:query] in order to include query params.
Filtering sensitive content
The Strava API uses an access key included in the HTTP
Authorization request header to authenticate requests. As I wanted to include the recorded fixture data in the public Git repository I had to remove this header. This is supported by the ExVCR config setting
filter_sensitive_data added to
config/test.exs. As shown below, I replace the Bearer token containing the access key with a placeholder value using a regular expression.
The config blacklists the
X-Request-Id response headers to ensure that these are also not recorded to prevent revealing sensitive data.
Including fixture data in source control allowed me to run the test suite on Travis CI.
Without using ExVCR, the tests would require a valid access key to use the Strava API. I would have to register a new Strava developer account, since each account only has a single key, and then use an environment variable to configure this setting. With recorded HTTP fixture data included in source control the tests suite runs quickly without being affected by external service availability.
config/test.exs mix configuration file I optionally include a
config/test.secret.exs file, if present. This file contains the private Strava API settings including my own access key. The file is excluded from source control by adding
/config/test.secret.exs to the
This approach also supports other developers who might want to contribute changes without requiring their own Strava API access key. They can clone the Git repository and immediately run the full test suite.
Running the Strava test suite without recorded HTTP fixture data.
$ mix test .................... Finished in 43.5 seconds 21 tests, 0 failures, 1 skipped
Now running the same tests with fixture data present results in a significant speed up.
$ mix test .................... Finished in 4.1 seconds 21 tests, 0 failures, 1 skipped
The total time reduced by a factor of ten: from 44 seconds down to 4 seconds.