volpe’s diary

フリーランスじゃなくなったプログラマ volpe が日々便利だなぁと感じたことを中心に綴るブログです

Rails で webmock と vcr の環境を作るメモ

Rails で外部WebサービスAPI を叩く spec を書く際に、HTTP通信を mock にしたくなったので webmock と vcr を使ってみた。

これらを使うと初回だけリアルな通信をして通信内容をファイルに保持してくれるので、2回目以降はリアルな通信なしに spec を動かすことが出来る。 保持したデータは git に登録しておけば他のメンバがテストを動かしてもリアルな通信が発生しない。これは便利!

ググればどこにでも書いてるような内容だけど、自分用に導入手順をメモしておく。

前提

  • rspec を使っている

導入手順

  • Gemfile の testwebmockvcr を追加して bundle install
  • spec/rails_helper.rbrequire 'webmock/rspec' を追加
  • spec/support/vcr.rb を追加して以下を記述
VCR.configure do |c|
  c.cassette_library_dir = 'spec/cassettes'
  c.hook_into :webmock
  c.configure_rspec_metadata!
  c.ignore_localhost = true
end
  • spec ファイルの scenario または describe に vcr の記述を追加。 (:hoge_fuga は任意の文字列)
scenario 'hoge の fuga を確認する', vcr: { cassette_name: :hoge_fuga } do

実行

  • 普通に rspec を実行すると1回目に spec/cassettes/hoge_fuga.yml が作成され、実際の通信のデータが記録される。 (初回はリアルな通信が走るので注意)
  • 2回目以降は上記ファイルのレスポンスを返すようになる
  • spec 毎に cassette_name を変えておけばそれぞれの通信内容を保持できる。
  • 必要なら生成された yml を直接編集して挙動を変えることも出来る。

トラブルシューティング

既存の Rails プロジェクトに webmockvcr を導入すると、すでに HTTPリクエストを行なっている spec で以下のように軒並みエラーになってしまう。

     VCR::Errors::UnhandledHTTPRequestError:

       ================================================================================
       An HTTP request has been made that VCR does not know how to handle:
         POST https://slack.com/api/auth.revoke

       There is currently no cassette in use. There are a few ways
       you can configure VCR to handle this request:

         * If you're surprised VCR is raising this error
           and want insight about how VCR attempted to handle the request,
           you can use the debug_logger configuration option to log more details [1].
         * If you want VCR to record this request and play it back during future test
           runs, you should wrap your test (or this portion of your test) in a
           `VCR.use_cassette` block [2].
         * If you only want VCR to handle requests made while a cassette is in use,
           configure `allow_http_connections_when_no_cassette = true`. VCR will
           ignore this request since it is made when there is no cassette [3].
         * If you want VCR to ignore this request (and others like it), you can
           set an `ignore_request` callback [4].

       [1] https://www.relishapp.com/vcr/vcr/v/5-0-0/docs/configuration/debug-logging
       [2] https://www.relishapp.com/vcr/vcr/v/5-0-0/docs/getting-started
       [3] https://www.relishapp.com/vcr/vcr/v/5-0-0/docs/configuration/allow-http-connections-when-no-cassette
       [4] https://www.relishapp.com/vcr/vcr/v/5-0-0/docs/configuration/ignore-request
       ================================================================================

いくつか対策は提示されているけど、既存はそのまま HTTPアクセスしちゃっていい場合は spec/support/vcr.rballow_http_connections_when_no_cassette = true を追加してあげる。

VCR.configure do |c|
  c.allow_http_connections_when_no_cassette = true   #++
  c.cassette_library_dir = 'spec/cassettes'
  c.hook_into :webmock
  c.configure_rspec_metadata!
  c.ignore_localhost = true
end

もしくは mock 化しちゃいたい場合は、vcr: { cassette_name: :xxxxxxxxxx } を付加してあげればいい。

参考

github.com

github.com