Detecting Unused VCR Cassettes
In the past 10 years, since I migrated to Ruby land, VCR has been one of the most frequently used tools in my toolbox. It allows us to easily cache 3rd party API responses (in VCR cassettes in YAML format) in tests so that we don't have to manually mock the responses with WebMock.
Usually, managing VCR cassettes is not a difficult task. But being a code reviewer, I'd noticed some PRs coming in with unused VCR cassettes and wanted a way for developers (including myself) to easily identify unused VCR cassettes.
Credits: This is not my own work. I picked it up in a GH Gist IIRC but only after many searches. But now I have misplaced the URL. I will update this post giving due credit to the author.
Features
- Runs only when profiling is enabled in RSpec
rspec -p
- Registers cassette usage by hooking into VCR
- Prints the difference of cassettes in the cassettes folder and the executed cassettes
Gotchas
- Will not recognise cassettes included in
xit
,xdescribe
etc as these examples will be skipped. - May give a false positive if a check fails before the VCR is called
Code
I have setup RSpec such that all files in the spec/support
folder to be auto required.
# File: spec/support/unused_cassette_formatter.rb
class UnusedCassetteFormatter
def initialize(output)
@output = output
end
def dump_profile(_)
return if (unused = list_unused).blank?
print(unused)
end
def self.use(cassette_name, options = {})
used_cassettes << VCR::Cassette.new(cassette_name, options).file
end
def self.used_cassettes
@used_cassettes ||= Set.new
end
private
def print(unused)
@output.puts("\nUnused cassettes:")
unused.each do |f|
relative_path = f.sub(Rails.root.to_s, '')
@output.puts(" ⚠️ #{relative_path}")
end
end
def list_unused
all = Dir[File.join(VCR.configuration.cassette_library_dir, '**/*.yml')].map do |d|
File.expand_path(d)
end
all - self.class.used_cassettes.to_a
end
end
RSpec.configure do |config|
unless config.files_to_run.one?
config.reporter.register_listener(UnusedCassetteFormatter.new(config.output_stream), :dump_profile)
end
end
module CassetteReporter
def insert_cassette(name, options = {})
UnusedCassetteFormatter.use(name, options)
super
end
end
VCR.extend(CassetteReporter)
Now when running rspec -p
you will have a response like this.
rspec -p
Unused cassettes:
⚠️ /spec/cassettes/purchase/bad_response.yml
⚠️ /spec/cassettes/purchase/bad_avs.yml
⚠️ /spec/cassettes/reveral/purchase.yml
About the Author
Ziyan Junaideen -
Ziyan is an expert Ruby on Rails web developer with 8 years of experience specializing in SaaS applications. He spends his free time he writes blogs, drawing on his iPad, shoots photos.