VCRってなんぞ?
github.com/vcrには
# Record your test suite's HTTP interactions and replay them during future test runs for fast, deterministic, accurate tests.
要約すると
テスト実行時にHTTPリクエストを送らないといけないとき最初の一回は本当にリクエストを飛ばすんだけどその時の通信内容を記録して2回目以降はHTTP通信をしないでテストができるようにするためのライブラリ。通信しないから早いし通信環境に左右されないテストができるよ。
今回はRailsでVCR使って思ったことや調べたことをまとめます。
想定読者
初めてVCR使うよ
複数のカセットを使う方法を探してるよ
VCRの使い方
基本的な使い方は本家githubに書いてあるんだけど
require 'rubygems'
require 'test/unit'
require 'vcr'
# 最初の設定する。
# 通信履歴の保存場所(VCRではカセットという。ややこしい(・ω・))
VCR.configure do |config|
config.cassette_library_dir = "fixtures/vcr_cassettes"
config.hook_into :webmock
end
# 初期設定はここまで
class VCRTest < Test::Unit::TestCase
def test_example_dot_com
VCR.use_cassette("synopsis") do
response = Net::HTTP.get_response(URI('http://www.iana.org/domains/reserved'))
assert_match /Example domains/, response.body
end
end
end
詳細
# 基本的には下記のコードで良い
VCR.use_cassette('好きなカセット名') do
# -------------------------------------
# この中にHTTP通信するコードを挟む
# 複数のURLにHTTP通信する場合でも深く考えなければ一つのカセットに保存してくれる
# もし複数の別々のカセットを扱いたい場合は後述
# -------------------------------------
end
.use_cassetteメソッドには引数が3つある
.use_cassette(name, options = {}, &block)
nameはカセット名
optionsにはカセット実行時に使うoptionをハッシュで入れられるようになっている
- erb: {カセットにerb形式で何かを埋め込める。URLパラメータとか}
- record: {新しいHTTPリクエストを飛ばすときに使う}
- match_requests_on {アクセスするURLパラメータが予想できない場合は正規表現などを使って処理をかける}
&blockは実際の処理を書く
複数のカセットを使う
結論: 入れ子にして使えば良いとなる。
確証はないんだけどRailsかRubyかVCRのバージョンによっては入れ子の順番が正しくないとVCRが正しく機能しない可能性があるようで会社のMacではカセットの順番を入れ替えると正しく機能しないが自宅のMacではうまく動いたりした。*知ってる方がおられたらぜひコメントください!
この入れ子の順番に関しては(この文は正しくないかもしれないです(・ω・))
処理する順番が中央に来る順序で記述すること
つまり
def some_method
http_request(1)
http_request(2)
http_request(3)
end
VCR.use_cassette('request_3') do
VCR.use_cassette('request_2') do
VCR.use_cassette('request_1') do
some_method(
end
end
end
自分の会社の環境だとこれで動いていた。
ただこれについてはVCRのリファレンスでもその方法について見つけられなかったのでなんとも確証がないなぁ
https://github.com/vcr/vcr/issues/306
一応コードを分割した通信に関しては上のリンクの方法が使えるようだけど一つのメソッドで来るんであるHTTP通信とはちょっと違うし
ネスト深くしたくない。メソッドを追加しよう!
Rubyはネストしてもそこまで読みにくいとは思わないけどネストはしたくない。
そこでVCRのコードをフォークしてなんとかネストしないで複数のカセットを使う方法を考えた。
そしてVCR.use_cassettesというメソッドをVCRに追加してみた
コード自体は大したことはしてないんだけども
# use_cassette 's' がつくので複数形!
# namesはカセットの配列。処理の順番に頭から書いていけばよいです。
def use_cassettes(names, &block)
return use_cassette(names.last) { block.call } if names.length == 1
cassette = names.pop
use_cassette(cassette) do
use_cassettes(names, &block)
end
end
まずnamesはカセットの配列を渡す
今回だと['request_1', 'request_2', 'request_3']
で&blockは今まで通り処理するメソッド。
メソッド内ではuse_cassetteメソッドを呼び出していてそれをカセットがなくなるまで再帰呼び出しする。
カセットが最後の1枚になったら&block引数に.callを付けて実際の処理を呼び出してreturn
これでネストせずに複数のカセットを利用したテストを書くことができる!
人生二回目のOSSへのPR
コードの出来に関してはあれだけど勉強の意味も込めてGemに含めてテスト追加して本家様にPR出してみた。
今のところなんのコメントもないんだけどなんか返信あったらいいなって思ってるです(´ω`)
11/17追記
マージしてもらったので最新のVCRでこの機能使うことができます!
使い所は少ないですが一つのサービスクラスのメソッドで複数箇所に通信を行う場合は使ってみてください/(^o^)\