Combining timecop and VCR during testing

Posted on

On almost every ruby project I work on, I always include the following two gems to make writing tests a dash easier:

  1. timecop - A really simple interface for travelling between times, and freezing at a particular time in your tests.
  2. vcr - Record and replay HTTP requests to the outside world. Perfect to speed up a test suite and allows me to work offline.

Combined, these allow me to capture a bunch of HTTP requests and replay them without having to worry about any time based conditions.

Example Usage

Setting up new customer on Stripe then confirming only 1 customer was created is a good use case for this:

# spec/acceptance/sign_up_spec.rb
require 'acceptance/acceptance_helper'

# Passing the `vcr: true` argument, tells vcr we'd like to record http requests in this feature.
feature 'Sign up', acceptance: true, vcr: true do
  # Use a `let` to store the time the test started
  let(:start_time){ VCR.current_cassette&.originally_recorded_at || Time.current }

  before do
    # Travel to when we first ran this test start_time )
    # [..] Any setup in the external API

  after do
    # [..] Any cleanup in the external APIs

  scenario 'With valid details, just one Stripe Customer is created' do
    expect(User.count).to eq(0)

    visit new_user_registration_path
    # [..] User completes a signup form

    expect(User.count).to eq(1)

    # Make sure just the one customer has been created since this test started
    expect(Stripe::Customer.all(created: { gte: start_time.to_i } ).count).to eq(1)

It's important to confirm an external API has worked as you expect, but constantly hitting an external API in your test suite is kind of unnecessary. Ideally, you should periodically delete the spec/vcr_cassettes folder to confirm your external API requests are still valid. But how often you do that is totally up to you :)

This was written by Mike Rogers, a freelance Ruby on Rails developer based in London.

Share the ♥ by sharing this!

If you want to discuss this post, feel free to tweet me (@MikeRogers0) or drop me an email. Any code samples unless stated otherwise are licensed under the The MIT License (MIT). Spotted a mistake? Send me a pull request :)