はじめに
VCRという gem を使うと、RSpecのテスト実行中に API を直接叩かず、代わりに過去のリクエスト・レスポンスの記録(カセットと呼ばれるYAMLファイル)を繰り返し使用することができます。
VCRには色んな設定項目がありますが、個人的には結局はシンプルな使い方しかしないことが多いので、VCRの基本機能を簡単に使える設定を考えてみました。
VCRの細かい機能については公式ドキュメントやネット上のブログ記事をご参照ください。
実行環境
- OS: LMDE 6 (faye) x86_64
- ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [x86_64-linux]
- rails-7.1.3.2
- webmock-3.23.1
- vcr-6.3.1
- rspec-core-3.13.0
- rspec-rails-6.1.3
やりたいこと
- RSpec の フック(hooks) を利用して VCR 機能の入り切りをする
- カセット(cassete)の名前を spec ファイル名を元に自動で生成
- spec ファイル名を元に自動的に適切なカセットを使用しVCR.use_cassetteを自動的に実行
- 環境変数、もしくは RSpec の メタデータ(metadata) からカセットに記録するかしないかを切替
- RSpec の メタデータ からVCRの設定をオーバーライドできるようにする
論より Run
手を動かして、色々試した結果をリポジトリに残しました。
設定ファイル
ここではspec/support/vcr.rb
を作り、そこにVCRの設定を記述しています。
require 'vcr'
# VCRの設定
VCR.configure do |c|
c.cassette_library_dir = 'spec/cassettes'
c.hook_into :webmock
c.ignore_localhost = true
c.ignore_hosts "chromedriver.storage.googleapis.com"
c.default_cassette_options = {
record: :once,
match_requests_on: %i[method host path query]
}
end
# RspecにVCR関連オプションを渡す仕組みと自動的にVCRを使いHTTP通信を記録する仕組みを定義
RSpec.configure do |c|
c.around(:each, :vcr) do |example|
cassette_path_segments = example.metadata[:file_path].sub(%r{.*/spec/}, '').sub('.rb', '').split(File::SEPARATOR)
cassette_path = File.join(cassette_path_segments)
case example.metadata
in { vcr: true }
vcr_overrides = {}
in { vcr: { **vcr_overrides } }
vcr_overrides[:record] = :new_episodes if vcr_overrides[:record] == true
end
vcr_overrides[:record] = :new_episodes if ENV['VCR_RECORD']
VCR.use_cassette(cassette_path, vcr_overrides) { example.call }
end
end
基本的な使い方
{ vcr: true }
を RSpec の メタデータ に渡すことにより、VCRの機能を有効化します。
describe ExampleApiClient, vcr: true do
describe "#list_todos" do
it "responds with 200 status" do
response = described_class.new.list_todos()
expect(response.status).to eq(200)
expect(response.body).to be_present
end
end
end
RSpecの仕様なのか :vcr
メタデータの渡し方は複数あります。
細かい設定を指定せずに VCR を有効化したい場合は、以下のどれでもOKです。
describe ExampleApiClient, :vcr do ...
describe ExampleApiClient, vcr: true do ...
describe ExampleApiClient, vcr: {} do ...
カセットに Web 通信を記録する方法
カセットに Web 通信を記録する方法は2パターン想定しています。
- 一時的に
{ vcr: { record: true } }
を RSpec の メタデータに渡す - 一時的に環境変数
VCR_RECORD=1
をつけてrspec
コマンドを実行する
カセットファイルは spec ファイルのファイル名を元に決定的に生成します。例えば、spec ファイルが ./spec/services/my_api_client_spec.rb
の場合、カセット名は./spec/cassettes/services/my_api_client_spec.yml
となります。
VCRオプションのオーバーライド
:record
オプションと:match_requests_on
オプションはメタデータに渡しオーバーライドできるようにしてあります。
describe ExampleApiClient, vcr: { record: :new_episodes } do
describe "#list_todos" do
it "responds with 200 status" do
# ...snip...
end
end
end
VCR の configure_rspec_metadata!
VCRにはconfigure_rspec_metadata!メソッドが存在し、ここで実施したことと似たようなことができるようです。
VCR.configure do |c|
# ...snip...
c.configure_rspec_metadata!
end
configure_rspec_metadata!の提供する機能で問題ないのであれば、1行のコードで解決しますが、configure_rspec_metadata!は公式ドキュメントに詳しく説明されておらず挙動がよくわかりません。ソースコードを読んでみましたが、それでもよくわかりませんでした。
個人的には10行程度のコードで自作できるのであれば、自分で書いたほうがいいんじゃないかな〜と思ってます。
おわりに
RSpecでVCRを簡単に使う方法を共有させていただきました。
なにか情報があれば、ぜひお便りください