[RSpec][VCR] WEB API呼び出しのテストをstubしてみる
- 2015年04月29日
- CATEGORY- 1. 技術力{技術情報}
松山です。
前回まで”MSXとKONAMIさん”と題して記事を書いていました。当初はドヤ顔で良いこと書いて締める予定だったのですが、月日の経過に伴いオチを失念したため連載3回という短命ですが打ち切りとさせてください。
「羽ばたけ未来へ!」
松山先生の次回作にご期待ください!
ということで、突然ですがVCRというgemを利用して、RSpecでのAPI呼び出しを簡単にstubする方法を書きます。
【やること】
・お天気webサービス(http://weather.livedoor.com/weather_hacks/webservice)を呼び出す処理の簡単なテストコードを書く
・テスト実行で通信が発生しないようVCRを利用してwebサービスの呼び出しをstubする
・テストコードがVCRに依存しない仕組みを作る
参考文献
・VCR(https://github.com/vcr/vcr)
・VCR – Relish(https://relishapp.com/vcr/vcr/v/2-9-3/docs)
環境
・ruby 2.1.3p242 (2014-09-19 revision 47630) [x86_64-darwin13.0]
・rspec 3.1.7
・rails 4.1.6
・vcr 2.9.3
・webmock 1.20.4
# Gemfile group :test do gem 'rspec-rails' gem 'vcr' gem 'webmock' end group :development, :test do gem 'rspec' gem 'byebug' end
【VCRの設定】
# spec/spec_helper.rb require 'webmock/rspec' require 'vcr' RSpec.configure do |config| VCR.configure do | c | c.allow_http_connections_when_no_cassette = true c.hook_into :webmock c.cassette_library_dir = 'spec/vcr_cassettes' c.default_cassette_options = { record: :new_episodes } c.before_record { | i | i.response.body.force_encoding 'UTF-8' } c.around_http_request do | request | VCR.use_cassette(request.parsed_uri.path, &request) end # c.debug_logger = $stdout end end
VCRのスタンダードな使い方は、
VCR.use_cassette('whatever cassette name you want') do # the body of the test would go here... end
となっており、このコードを各テストケースに埋める必要があります。これはすこーし煩わしく、テストコードがVCRに依存するのでイケナイです。上記spec/spec_helper.rbの設定は全てのAPI呼び出し(※1)をVCRによってstubするもので、テストコードの非VCR依存を実現します。
※1正確にはWebMockによるNet::HTTPのフック
【モデルを作る】
# app/models/weather.rb class Weather < ActiveResource::Base self.site = 'http://weather.livedoor.com/' self.prefix = '/forecast/webservice/json' self.format = :json self.include_format_in_path = false self.element_name = '' def self.forecast(params) self.new get("v1", params) end end
ActiveResource::Baseを継承した「お天気」に該当するモデルです。ちなみにお天気APIは、
http://weather.livedoor.com/forecast/webservice/json/v1?city=400040
のように呼び出せますが、これはRestfulではないのでモデルでごにょごにょしています。
動作確認
$ rails c > Weather.forecast city:'400040' => #<Weather:0x007fd3741a3a70 @attributes={... > Weather.forecast(city:'400040').pinpointLocations.first.name => "大牟田市"
この動作確認により、spec/vcr_cassettes/forecast/webservice/json/v1.yml というファイルが自動生成されます(ファイルの命名規則は前述のspec/spec_helper.rbによるもの)。これはVCRの”カセット”と呼ばれるもので、お天気APIのリクエストとレスポンスを記録したファイルです。
・初回リクエスト時は通信を行いカセットを生成(recording)
・次回以降は通信が発生しない、カセット再生によるstubリクエストを発行(playback)
というのがVCRの基本動作になります。
【RSpecでテストを書く】
# spec/model/weather_spec.rb describe Weather do describe '.forecast' do subject { Weather.forecast(params) } context 'city-id is 020020' do let(:params) { { city: '020020' } } describe '.title' do it { expect(subject.title).to eq '青森県 むつ の天気' } end end end end
実行結果
Weather .forecast city-id is 020020 .title should eq "青森県 むつ の天気" Finished in 0.0248 seconds (files took 84 minutes 34 seconds to load) 1 example, 0 failures [Finished in 0.7s]
至って普通のテストコードです。VCR使うぞー!という記述は一切ありません。ステキです。
今回はここまでです。
次回以降は今回のソースコードをベースにVCRで出来ることを掘り下げていきながら、最終的にはWEB API呼び出しにおけるテストのベストプラクティスを模索したいと思いまっす。
みなさんiOSのアップデートはお済みですか(執筆時は2015.03)。回線が不調なのか、なかなか終わりません。
【関連記事】
MSXとKONAMIさん
MSXとKONAMIさん(その2)
MSXとKONAMIさん(その3)
- 2015年04月29日
- CATEGORY- 1. 技術力{技術情報}