16
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

VCRで複数のカセットを使う方法について悩んだのでPR出してみた話

Last updated at Posted at 2017-09-24

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^)\

16
2
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?