RSpecでWebMockとVCRを使ったテストを書く
はじめに
表題の通り、RSpecでWebMockとVCRを使ったテストを初めて書いたのでその学びをまとめます。
環境
各gem
WebMock
外部APIを叩くアプリにおいて、テストを書く際に擬似的なAPIのレスポンスを作成し、そのレスポンスを元にテストを実行します。
その際のAPIレスポンス(モック)を定義できるgem。
VCR
上記のモックは基本的には自分で書いて用意しますが、その手間を省けるgem。
モックデータが存在していなければ、実際にAPIを叩いてそのリクエストとレスポンスをYAMLファイルとして自動で作成してくれる。
以降はそのYAMLファイルをモックとしてテストを実行できる。
セットアップ
Gemfile
に以下を追加しbundle
コマンド実行。
gem "webmock" gem "vcr"
spec/rails_helper.rb
に下記を追加し、RSpecでWebMockを使えるようにする。
require "webmock/rspec"
VCRの設定
spec/spec_helper.rb
にVCRの設定を書いていきます。
require "vcr" # ... VCR.configure do |c| c.cassette_library_dir = "spec/vcr" c.hook_into :webmock c.allow_http_connections_when_no_cassette = true c.configure_rspec_metadata! c.ignore_localhost = true c.ignore_hosts "chromedriver.storage.googleapis.com" c.default_cassette_options = { record: :new_episodes, match_requests_on: [:method, :path, :query, :body] } end
VCRはモックデータを「カセット」と呼ぶので、cassette = モックデータ
であり、カセットを使用することを「再生」と呼んでいます(シャレだ...)。
各設定については以下の通り。
cassette_library_dir = "spec/vcr"
...カセットを保存するルートディレクトリhook_into :webmock
...利用するモックライブラリを指定allow_http_connections_when_no_cassette = true
...VCRを使わない場所ではHTTP通信を許可するconfigure_rspec_metadata!
...RSpecとの連携ignore_localhost = true
...ローカルホストのリクエストを無視し、Capybaraのリクエストに干渉しないようにするignore_hosts "chromedriver.storage.googleapis.com"
...指定したホスト(chromedriver.storage.googleapis.com)へのリクエストを無視するrecord: :new_episodes
...カセットがなければAPIをコールしてそれを記録するmatch_requests_on: [:method, :path, :query, :body]
...カセットを引き当てる条件。今回はリクエストのメソッドとパス、 クエリそしてリクエストボディが一致するカセットを再生するという意味。
テストコードでVCRを使用
外部APIへのリクエストが生じるテストコードでVCRを使用するには以下のようにカセット名を指定します。
# テストコードを一部抜粋 describe "記事一覧", vcr: "twitter_api_response" do it "user_1の記事が表示される" do visit articles_path expect(page).to have_content "2020-10-17" end end
もしくは以下のような書き方の方が明示的で読みやすい。
describe "記事一覧", vcr: { cassette_name: "twitter_api_response" } do it "user_1の記事が表示される" do visit articles_path expect(page).to have_content "2020-10-17" end end
これでテストを実行すると、指定したカセットが存在していなければAPIを叩いてそのレスポンスをYAMLファイルで保存してくれます。
今回の場合はspec/vcr/twitter_api_response.yml
が保存されます。
次またテストを実行するとこのファイルをモックとして参照してテストを実行してくれます。便利。
モックに秘密情報を載せたくない場合
今回はTwitter APIを叩いてテストを実行していますが、保存されたカセットにはリクエスト時のAPI Keyとアクセストークンが平文で記載され保存されていたので、これをいい感じにマスクしたい。
VCRの設定に以下を追記する。
# spec/spec_helper.rb VCR.configure do |c| # 省略 c.filter_sensitive_data("<API_KEY>") { ENV["TWITTER_API_KEY"] } c.filter_sensitive_data("<ACCESS_TOKEN>") { ENV["ACCESS_TOKEN"] } end
隠したい文字列(API Keyやアクセストークン)を完全一致で指定します。今回の例でいうとENV["TWITTER_API_KEY"]
の部分。
実際にはこの環境変数にAPI Keyを格納し、この設定上でも環境変数で指定しておく。
こうしておくとカセットに平文で乗っていたAPI Keyそのものが"<API_KEY>"
という文字に置き換わる。
カセットを一度削除してからもう一度テストを実行すると上記の設定が反映されたモックが作成される。
参考
- bblimke/webmock: Library for stubbing and setting expectations on HTTP requests in Ruby.
- vcr/vcr: Record your test suite's HTTP interactions and replay them during future test runs for fast, deterministic, accurate tests.
- VCRでWeb APIのモックを楽しよう! - アクトインディ開発者ブログ
- VCR gemでアクセストークンとかをいいかんじにする術 - kitak blog
- VCR で外部 API へのリクエストをダンプするときに機密情報をマスクしたい - Qiita
- 【動画付き】Everyday RailsのサンプルアプリをRails 6で動かす際に必要なテストコードの変更点 - give IT a try