Edited at

vcrでコマンドライン(環境変数)でrecordモードを切り替える、他

More than 3 years have passed since last update.

基本的な使い方は以下を参照

vcr (GitHub)

documentation (Relish)

RailsCasts #291 Testing with VCR


vcrでコマンドラインからrecordモードを切り替える

コマンドラインから以下のように、recordモードを上書きできる

$ RECORD=all rspec spec/foo_spec.rb

$ RECORD=new_episodes rspec spec/bar_spec.rb:15

spec_helperなどに以下を記述 (default_cassette_optionsの部分)


spec/spec_helper.rb

VCR.configure do |c|

c.cassette_library_dir = 'spec/fixtures/vcr_cassettes'
c.hook_into :webmock # or :fakeweb

c.default_cassette_options = { record: ENV.fetch('RECORD'){ :once }.to_sym }
end


これをしないと、記録状態を変更しようとするたびに describe "foo", record: :all do といったrecordオプションを付加しないといけないのでわずらわしい。(通常commit前に消す必要もあり面倒)

参考1 参考2 参考3


vcrのcassettesの生成した内容を見やすく変換する

vcrのcassettesのymlファイルを見てもレスポンスのbodyが読めない

Guardのプラグインを使って、ymlの隣にtxtを出力する (webmockは動作確認済み)

puts, f.writeの部分は好きなように変更すると良い


Guardfile

require 'guard/plugin'

require 'json'

module ::Guard
class VcrReader < ::Guard::Plugin
def run_all
target_path = 'spec/fixtures/vcr_cassettes'
puts "Converting all request/response data in #{target_path}."
Dir.glob("#{target_path}/**/*\.yml").each do |path|
generate_pretty_log_from_yml(path)
end
end

def run_on_changes(paths)
puts "Converting request/response data. #{paths.inspect}"
paths.each do |path|
next unless File.exists?(path)
log_path = generate_pretty_log_from_yml(path)
puts "Generated #{log_path}"
end
end

private

def generate_pretty_log_from_yml(path)
record = YAML.load_file(path)
log_path = path.gsub(/\.yml/, '.txt')
File.open(log_path, 'w') do |f|
record["http_interactions"].each do |interaction|
request_method = interaction["request"]["method"]

f.write('='*80+"\n")
f.write('Request '+'-'*80+"\n")
f.write(interaction["recorded_at"]+"\n")
f.write("#{request_method.upcase} #{interaction["request"]["uri"]}"+"\n")
f.write(arrange_body_string interaction["request"]["body"]["string"]) if request_method == 'post'
f.write("\n")
f.write('Response '+'-'*80+"\n")
f.write(arrange_body_string interaction["response"]["body"]["string"])
f.write("\n")
end
end
log_path
end

def arrange_body_string(string)
begin
JSON.pretty_generate JSON.parse(string)
rescue
string
end
end
end
end

guard :vcr_reader do
watch(%r{^spec/fixtures/vcr_cassettes/.+\.yml$})
end


出力の変換結果例

================================================================================

Request --------------------------------------------------------------------------------
Tue, 08 Jul 2014 04:02:06 GMT
POST http://test.com/login.json
{
"body": {
"login": "test_user",
"password": "secret"
}
}
Response --------------------------------------------------------------------------------
{
"body": {
"loign": "test_user",
"code": "ABC",
"name": "テストユーザー",
"lang": "ja"
},
"status": "success"
}
================================================================================
Request --------------------------------------------------------------------------------

...


録画した内容(cassettes内のリクエスト)の一致ルール match_requests_on


日時、日付など動的な特定のパラメータの値を無視するようにする

日時、日付など特定のパラメータの値をテストで使っていると、日付が変わったり、年度が変わったりするとcassetteが見つからないエラーで失敗するようになるテストができる。(例: date, starts_at, start_time, yearなど)

日付以外にも、毎回変わるものの例に新規作成で発行されたIDなどがある(createしてそのIDを元に以降のページをテストしている場合など)

それらのパラメータをcassetteのマッチング時に無視するような設定は以下のようにできる。

describe "SomeTest with Date", vcr: true, match_requests_on: [:method, VCR.request_matchers.uri_without_param(:date)] do

URI without param(s)

具体的なエラーの例

VCR::Errors::UnhandledHTTPRequestError:

========================================
An HTTP request has been made that VCR does not know how to handle
GET http://example.com/foo?date=20151119&...


パラメータ全体を無視する

パラメータ全体を無視することも可能

optionの:body, :queryなどを除く (match_requests_on: [:method, :path])

ただし、この場合は副作用が起こることがある。

同じpathに対して違うリクエストをするものが同一cassettesに入った時などに、すべて同じ(最初にマッチした)リクエストが返されてしまう。


match_requests_onの設定を調べる

各specの各exampleでどの設定になっているかを調べるには

(注. do |example| に変更)

  it "..." do |example|

puts example.metadata[:match_requests_on]
...

デフォルトの設定を調べるにはVCR.configuration.default_cassette_optionsVCR.configurationなどを出力してみると良い


他のパターン



  • :pathを使えば違う環境(ドメイン)に対して動かすことができる


    • 例えば、http://stage1.example.com/, http://stage2.example.com/などの複数環境が混在している場合など



Custom VCR matchers


その他configurationオプション


vcrのcassettesの名前をカスタマイズする

rspecのexampleの文字をファイルのパスにする

RailsCasts #291 Testing with VCR から

RSpec.configure do |c|

c.around(:each, :vcr) do |example|
name = example.metadata[:full_description].split(/\s+/, 2).join("/").underscore.gsub(/[^\w\/]+/, "_")
options = example.metadata.slice(:record, :match_requests_on).except(:example_group)
VCR.use_cassette(name, options) { example.call }
end
end

例えば spec/fixtures/vcr_cassettes/zip_code_lookup/show_beverly_hills_given_90210.yml のようなファイル名になる

ファイル名の文字数を制限する

gemの中でvcrを使っていたときに、そのgemをプロジェクトからbundleするとGem::Package::TooLongFileNameが出ることがあった

rubygemsでファイルの長さが100文字に制限されているらしい

ファイルを最大文字数でカットする

c.around(:each, :vcr) do |example|

name = example.metadata[:full_description].split(/\s+/, 2).join("/").underscore.gsub(/[^\w\/]+/, "_")
# NOTE Fix 'Gem::Package::TooLongFileName'
name = name.split('/').map{|s| s[0..93] }.join('/')

StackOverflow : Building Rails 3 Engine Throwing Gem::Package::TooLongFileName Error


vcrのcassettesの名前にspec名を追加する

利点としては、特定のspecのcassettesを探したり削除しやすくなる

spec/features/foo/bar_spec.rbなら

spec/fixtures/vcr_cassettes/foo/bar/xxx_description_yyy.ymlのようになる

c.around(:each, type: :feature) do |example|

spec_name = example.metadata[:file_path].gsub('./spec/features/', '').gsub('_spec.rb', '')
desc_name = example.metadata[:full_description].split(/\s+/, 2).join("/").underscore.gsub(/[^\w\/]+/, "_")
name = [spec_name, desc_name].join("/")


使われていないcassettesを削除する

Cleaning up unused cassettes


vcrをRAILS_ENV=developmentで使う


vcr_cable

vcr_cable

gem追加後、

$ bundle exec rails generate vcr_cable


config/vcr_cable.yml

development:

hook_into: webmock
cassette_library_dir: spec/fixtures/vcr_cassettes
enable_erb: false
allow_playback_repeats: false
allow_http_connections_when_no_cassette: true
enable_vcr_cable: true


有効にする方法は、enable_vcr_cable: trueにするか、ENABLE_VCR_CABLE=true bundle exec rails sでサーバーを起動する

意外にちゃんと動いた。あくまでモックの扱いなので、動的なアクション・データを扱いたい場合は使えなくなる。

spec/fixtures/vcr_cassettes/vcr_cable_cassette.ymlにカセットができるが、カセットが一つなのでファイルが肥大して問題になることもありそう。


他見つけたやつ。古い、試していない vcr-remote-controller


Rack::VCR

新しい

Rack::VCRでらくらくアプリケーション間テスト

rack-vcr

rack-vcr-sample


vcrのcassettes一覧を管理画面インターフェースで閲覧、削除する

mr_video

サブディレクトリに対応した模様

そのままだとcassette_library_dirの直下しか取り扱わない挙動

ディレクトリ配下を対象にする以下のIssueがある


Add Support for cassette_library_dir with nested folders


リクエストパラメータのArrayに括弧をつけない